| --- |
| jupytext: |
| text_representation: |
| extension: .md |
| format_name: myst |
| kernelspec: |
| name: python3 |
| display_name: Python 3 |
| --- |
| <!--- |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| --> |
| |
| # Joins |
| |
| DataFusion supports the following join variants via the method {py:func}`~datafusion.dataframe.DataFrame.join` |
| |
| - Inner Join |
| - Left Join |
| - Right Join |
| - Full Join |
| - Left Semi Join |
| - Left Anti Join |
| |
| For the examples in this section we'll use the following two DataFrames |
| |
| ```{code-cell} ipython3 |
| from datafusion import SessionContext |
| |
| ctx = SessionContext() |
| |
| left = ctx.from_pydict( |
| { |
| "customer_id": [1, 2, 3], |
| "customer": ["Alice", "Bob", "Charlie"], |
| } |
| ) |
| |
| right = ctx.from_pylist([ |
| {"id": 1, "name": "CityCabs"}, |
| {"id": 2, "name": "MetroRide"}, |
| {"id": 5, "name": "UrbanGo"}, |
| ]) |
| ``` |
| |
| ## Inner Join |
| |
| When using an inner join, only rows containing the common values between the two join columns present in both DataFrames |
| will be included in the resulting DataFrame. |
| |
| ```{code-cell} ipython3 |
| left.join(right, left_on="customer_id", right_on="id", how="inner") |
| ``` |
| |
| The parameter `join_keys` specifies the columns from the left DataFrame and right DataFrame that contains the values |
| that should match. |
| |
| ## Left Join |
| |
| A left join combines rows from two DataFrames using the key columns. It returns all rows from the left DataFrame and |
| matching rows from the right DataFrame. If there's no match in the right DataFrame, it returns null |
| values for the corresponding columns. |
| |
| ```{code-cell} ipython3 |
| left.join(right, left_on="customer_id", right_on="id", how="left") |
| ``` |
| |
| ## Full Join |
| |
| A full join merges rows from two tables based on a related column, returning all rows from both tables, even if there |
| is no match. Unmatched rows will have null values. |
| |
| ```{code-cell} ipython3 |
| left.join(right, left_on="customer_id", right_on="id", how="full") |
| ``` |
| |
| ## Left Semi Join |
| |
| A left semi join retrieves matching rows from the left table while |
| omitting duplicates with multiple matches in the right table. |
| |
| ```{code-cell} ipython3 |
| left.join(right, left_on="customer_id", right_on="id", how="semi") |
| ``` |
| |
| ## Left Anti Join |
| |
| A left anti join shows all rows from the left table without any matching rows in the right table, |
| based on a the specified matching columns. It excludes rows from the left table that have at least one matching row in |
| the right table. |
| |
| ```{code-cell} ipython3 |
| left.join(right, left_on="customer_id", right_on="id", how="anti") |
| ``` |
| |
| ## Duplicate Keys |
| |
| It is common to join two DataFrames on a common column name. Starting in |
| version 51.0.0, `` datafusion-python` `` will now coalesce on column with identical names by |
| default. This reduces problems with ambiguous column selection after joins. |
| You can disable this feature by setting the parameter `coalesce_duplicate_keys` |
| to `False`. |
| |
| ```{code-cell} ipython3 |
| left = ctx.from_pydict( |
| { |
| "id": [1, 2, 3], |
| "customer": ["Alice", "Bob", "Charlie"], |
| } |
| ) |
| |
| right = ctx.from_pylist([ |
| {"id": 1, "name": "CityCabs"}, |
| {"id": 2, "name": "MetroRide"}, |
| {"id": 5, "name": "UrbanGo"}, |
| ]) |
| |
| left.join(right, "id", how="inner") |
| ``` |
| |
| In contrast to the above example, if we wish to get both columns: |
| |
| ```{code-cell} ipython3 |
| left.join(right, "id", how="inner", coalesce_duplicate_keys=False) |
| ``` |
| |
| ## Disambiguating Columns with `DataFrame.col()` |
| |
| When both DataFrames contain non-key columns with the same name, you can use |
| {py:meth}`~datafusion.dataframe.DataFrame.col` on each DataFrame **before** the |
| join to create fully qualified column references. These references can then be |
| used in the join predicate and when selecting from the result. |
| |
| This is especially useful with {py:meth}`~datafusion.dataframe.DataFrame.join_on`, |
| which accepts expression-based predicates. |
| |
| ```{code-cell} ipython3 |
| left = ctx.from_pydict( |
| { |
| "id": [1, 2, 3], |
| "val": [10, 20, 30], |
| } |
| ) |
| |
| right = ctx.from_pydict( |
| { |
| "id": [1, 2, 3], |
| "val": [40, 50, 60], |
| } |
| ) |
| |
| joined = left.join_on( |
| right, left.col("id") == right.col("id"), how="inner" |
| ) |
| |
| joined.select(left.col("id"), left.col("val"), right.col("val")) |
| ``` |