| --- | |
| 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")) | |
| ``` |