{
"cells": [
{
"cell_type": "markdown",
"id": "2239e303-b5eb-4c03-bf1a-9817e6c5153a",
"metadata": {},
"source": [
"# 複数のデータフレームの結合"
]
},
{
"cell_type": "markdown",
"id": "6fd0e560-abfa-4017-9cfc-263f18488eb4",
"metadata": {},
"source": [
"複数のデータフレームを結合する操作は、データの統合や分析において非常に重要な手段です。Polarsでは、さまざまな結合方法が提供されており、データの構造に応じて最適な方法を選択することができます。以下では、Polarsで利用できる代表的な結合手法である`concat`、`join`、`join_asof`、`join_where`とデータフレームの結合メソッドについて紹介します。"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "c2d4da51-0d23-47f3-a0b1-7270b4e8afa2",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import polars as pl\n",
"from helper.jupyter import row"
]
},
{
"cell_type": "markdown",
"id": "802062ef-ec51-4e7d-8496-8766896d7020",
"metadata": {},
"source": [
"## concat"
]
},
{
"cell_type": "markdown",
"id": "e753d479-1654-4927-a3cf-8a74ef143eeb",
"metadata": {},
"source": [
"`pl.concat()`を使用すると、複数のデータフレームを縦または横に結合できます。結合方法は引数`how`で指定され、以下の5種類の結合方法があります。\n",
"\n",
"* `vertical`および`vertical_relaxed`: 縦方向の結合\n",
"* `horizontal`: 横方向の結合\n",
"* `diagonal`および`diagonal_relaxed`: 縦横両方向の結合\n",
"* `align`: 縦横両方向の結合ですが、データをキーで整列してから結合を行います\n",
"\n",
"以下は、次の二つのデータフレームを使って、上記の結合方法について詳しく説明します。"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "5c2ecb72-ca11-4f81-a451-960889179c1f",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"df1 = pl.DataFrame({\"x\":[1, 2, 3], \"y\":[2, 3, 1]})\n",
"df2 = pl.DataFrame({\"x\":[6, 2, 1, 5], \"y\":[12, 3, 2, 4]})\n",
"row(df1, df2)"
]
},
{
"cell_type": "markdown",
"id": "3cdeb222-9d83-4810-b81c-dcd6dd18eed4",
"metadata": {},
"source": [
"### 縦結合"
]
},
{
"cell_type": "markdown",
"id": "81afec5e-e027-4fd3-a490-dc51825ec049",
"metadata": {},
"source": [
"以下のように、すべてのデータフレームの列名とデータ型が一致する場合は、`vertical`で縦に結合します。"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "a4f8b0a9-6f2a-47f3-819d-9c143f6efba5",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
"shape: (7, 2)\n",
"┌─────┬─────┐\n",
"│ x ┆ y │\n",
"│ --- ┆ --- │\n",
"│ i64 ┆ i64 │\n",
"╞═════╪═════╡\n",
"│ 1 ┆ 2 │\n",
"│ 2 ┆ 3 │\n",
"│ 3 ┆ 1 │\n",
"│ 6 ┆ 12 │\n",
"│ 2 ┆ 3 │\n",
"│ 1 ┆ 2 │\n",
"│ 5 ┆ 4 │\n",
"└─────┴─────┘"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pl.concat([df1, df2], how='vertical')"
]
},
{
"cell_type": "markdown",
"id": "d9867f03-4532-4375-b0ac-eb161f18654d",
"metadata": {},
"source": [
"列名が一致するがデータ型が一致しない場合は、`vertical_relaxed`を使用して縦に結合します。この場合、結果のデータ型は上位のデータ型が採用されます。以下のコード例では、`df2`の`x`列を`Float64`型にキャストしてから結合しています。このように、`x`列のデータ型が`Float64`に統一され、縦に結合されます。"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "39270b04-4119-403c-9323-292eb99bf6c3",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"
shape: (7, 2)x | y |
---|
f64 | i64 |
1.0 | 2 |
2.0 | 3 |
3.0 | 1 |
6.0 | 12 |
2.0 | 3 |
1.0 | 2 |
5.0 | 4 |
"
],
"text/plain": [
"shape: (7, 2)\n",
"┌─────┬─────┐\n",
"│ x ┆ y │\n",
"│ --- ┆ --- │\n",
"│ f64 ┆ i64 │\n",
"╞═════╪═════╡\n",
"│ 1.0 ┆ 2 │\n",
"│ 2.0 ┆ 3 │\n",
"│ 3.0 ┆ 1 │\n",
"│ 6.0 ┆ 12 │\n",
"│ 2.0 ┆ 3 │\n",
"│ 1.0 ┆ 2 │\n",
"│ 5.0 ┆ 4 │\n",
"└─────┴─────┘"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pl.concat([\n",
" df1, \n",
" df2.with_columns(pl.col('x').cast(pl.Float64))\n",
" ], \n",
" how='vertical_relaxed')"
]
},
{
"cell_type": "markdown",
"id": "159c15d6-c660-413a-a1b6-22fccd452d5b",
"metadata": {},
"source": [
"Pandasのように縦結合するとき、各データフレームにキーを付ける方法についてのプログラム例を以下に示します。この方法では、`df1`と`df2`にそれぞれキーを付けてから縦に結合します。\n",
"プログラムには、`key`列を追加して各データフレームの行にキーを付けてから縦に結合することで、元のデータフレームを識別できるようにしています。"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "ab293f02-6f23-4740-8e20-d22f4d5c9755",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"
shape: (7, 3)key | x | y |
---|
str | i64 | i64 |
"A" | 1 | 2 |
"A" | 2 | 3 |
"A" | 3 | 1 |
"B" | 6 | 12 |
"B" | 2 | 3 |
"B" | 1 | 2 |
"B" | 5 | 4 |
"
],
"text/plain": [
"shape: (7, 3)\n",
"┌─────┬─────┬─────┐\n",
"│ key ┆ x ┆ y │\n",
"│ --- ┆ --- ┆ --- │\n",
"│ str ┆ i64 ┆ i64 │\n",
"╞═════╪═════╪═════╡\n",
"│ A ┆ 1 ┆ 2 │\n",
"│ A ┆ 2 ┆ 3 │\n",
"│ A ┆ 3 ┆ 1 │\n",
"│ B ┆ 6 ┆ 12 │\n",
"│ B ┆ 2 ┆ 3 │\n",
"│ B ┆ 1 ┆ 2 │\n",
"│ B ┆ 5 ┆ 4 │\n",
"└─────┴─────┴─────┘"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"data = {\"A\":df1, \"B\":df2}\n",
"pl.concat([\n",
" df.select(pl.lit(key).alias(\"key\"), pl.all()) \n",
" for key, df in data.items()\n",
"])"
]
},
{
"cell_type": "markdown",
"id": "0a1415f5-75ed-4381-aeda-d0c422edf720",
"metadata": {},
"source": [
"### 横結合"
]
},
{
"cell_type": "markdown",
"id": "f524f6a9-3b79-475f-981b-d231b1327020",
"metadata": {},
"source": [
"列名が異なるデータフレームを横に結合するには、`horizontal`を使用します。以下のプログラムでは、`df1`と`df2`の列名を2種類の方法でリネームし、横結合します。このように、`df1`の列名に`1`を、`df2`の列名に`2`を付けて横に結合します。`df2`は`df1`より行数が多いため、`df1`に存在しない行には`null`が補完されます。"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "3b7d517a-a272-4e1f-bca2-d8a76da1cb03",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"
shape: (4, 4)x1 | y1 | x2 | y2 |
---|
i64 | i64 | i64 | i64 |
1 | 2 | 6 | 12 |
2 | 3 | 2 | 3 |
3 | 1 | 1 | 2 |
null | null | 5 | 4 |
"
],
"text/plain": [
"shape: (4, 4)\n",
"┌──────┬──────┬─────┬─────┐\n",
"│ x1 ┆ y1 ┆ x2 ┆ y2 │\n",
"│ --- ┆ --- ┆ --- ┆ --- │\n",
"│ i64 ┆ i64 ┆ i64 ┆ i64 │\n",
"╞══════╪══════╪═════╪═════╡\n",
"│ 1 ┆ 2 ┆ 6 ┆ 12 │\n",
"│ 2 ┆ 3 ┆ 2 ┆ 3 │\n",
"│ 3 ┆ 1 ┆ 1 ┆ 2 │\n",
"│ null ┆ null ┆ 5 ┆ 4 │\n",
"└──────┴──────┴─────┴─────┘"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pl.concat([\n",
" df1.rename(lambda name:f\"{name}1\"), \n",
" df2.select(pl.all().name.suffix(\"2\"))\n",
"], how='horizontal')"
]
},
{
"cell_type": "markdown",
"id": "83c88796-5c82-47dd-8203-1c054efe81d8",
"metadata": {},
"source": [
"一部の列名が同じで、一部の列名が異なる場合、`diagonal`や`diagonal_relaxed`を使用して結合できます。`diagonal_relaxed`は自動的に上位のデータ型を採用します。次のプログラムでは、`df1`に`u`列が追加され、`df2`に`v`列が追加され、`diagonal`で二つのデータフレームを結合します。列名が一致するデータは縦に結合し、一致しない列は`NULL`で欠損値を表します。"
]
},
{
"cell_type": "markdown",
"id": "ecdf3c1e-8c47-4bb6-8d38-ef3c09bbe6c2",
"metadata": {},
"source": [
"### 縦と横結合"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "4c78c54d-18fe-42f0-bf3c-c0604e1f0483",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"
shape: (7, 4)x | y | u | v |
---|
i64 | i64 | i64 | i64 |
1 | 2 | 3 | null |
2 | 3 | 5 | null |
3 | 1 | 4 | null |
6 | 12 | null | 72 |
2 | 3 | null | 6 |
1 | 2 | null | 2 |
5 | 4 | null | 20 |
"
],
"text/plain": [
"shape: (7, 4)\n",
"┌─────┬─────┬──────┬──────┐\n",
"│ x ┆ y ┆ u ┆ v │\n",
"│ --- ┆ --- ┆ --- ┆ --- │\n",
"│ i64 ┆ i64 ┆ i64 ┆ i64 │\n",
"╞═════╪═════╪══════╪══════╡\n",
"│ 1 ┆ 2 ┆ 3 ┆ null │\n",
"│ 2 ┆ 3 ┆ 5 ┆ null │\n",
"│ 3 ┆ 1 ┆ 4 ┆ null │\n",
"│ 6 ┆ 12 ┆ null ┆ 72 │\n",
"│ 2 ┆ 3 ┆ null ┆ 6 │\n",
"│ 1 ┆ 2 ┆ null ┆ 2 │\n",
"│ 5 ┆ 4 ┆ null ┆ 20 │\n",
"└─────┴─────┴──────┴──────┘"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dfs = [\n",
" df1.with_columns(u=pl.col('x') + pl.col('y')),\n",
" df2.with_columns(v=pl.col('x') * pl.col('y'))\n",
"]\n",
"pl.concat(dfs, how='diagonal')"
]
},
{
"cell_type": "markdown",
"id": "f3c2a603-d129-4e5f-9e9e-16c2a5291719",
"metadata": {},
"source": [
"### 整列結合"
]
},
{
"cell_type": "markdown",
"id": "cc1c03f2-15c4-4e72-ab4b-b49ee1dccb9e",
"metadata": {},
"source": [
"`align`結合は、`diagonal`と似ていますが、列名が一致するデータをキーとして集合化し、他の列の値を統合します。以下は、`df1`と`df2`に追加した列を使って`align`で結合する例です。このように、`align`結合では共通の`x`と`y`の値をキーとして行をマージし、他の列の値を統合しています。例えば、`x=1, y=2`の行は`u=3`と`v=2`が統合されて1行になります。"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "0cafc3ad-5a6b-43ae-8e71-b50436fab760",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"
shape: (5, 4)x | y | u | v |
---|
i64 | i64 | i64 | i64 |
1 | 2 | 3 | 2 |
2 | 3 | 5 | 6 |
3 | 1 | 4 | null |
5 | 4 | null | 20 |
6 | 12 | null | 72 |
"
],
"text/plain": [
"shape: (5, 4)\n",
"┌─────┬─────┬──────┬──────┐\n",
"│ x ┆ y ┆ u ┆ v │\n",
"│ --- ┆ --- ┆ --- ┆ --- │\n",
"│ i64 ┆ i64 ┆ i64 ┆ i64 │\n",
"╞═════╪═════╪══════╪══════╡\n",
"│ 1 ┆ 2 ┆ 3 ┆ 2 │\n",
"│ 2 ┆ 3 ┆ 5 ┆ 6 │\n",
"│ 3 ┆ 1 ┆ 4 ┆ null │\n",
"│ 5 ┆ 4 ┆ null ┆ 20 │\n",
"│ 6 ┆ 12 ┆ null ┆ 72 │\n",
"└─────┴─────┴──────┴──────┘"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pl.concat(dfs, how=\"align\")"
]
},
{
"cell_type": "markdown",
"id": "6f885c89-2eec-42a2-9a9e-d4f543749a04",
"metadata": {},
"source": [
"`pl.align_frames()`を使用すると、複数のデータフレームを指定した列で整列させることができます。以下の例では、`df1`と`df2`を`x`および`y`列で整列させています。整列後の各データフレームの行数は同じで、指定された列の値に基づいて他の列が整列されています。"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "3eae433d-dd50-4c57-80b5-0c49af81c8bd",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" shape: (5, 3)x | y | u |
---|
i64 | i64 | i64 | 1 | 2 | 3 | 2 | 3 | 5 | 3 | 1 | 4 | 5 | 4 | null | 6 | 12 | null |
| \n",
" shape: (5, 3)x | y | v |
---|
i64 | i64 | i64 | 1 | 2 | 2 | 2 | 3 | 6 | 3 | 1 | null | 5 | 4 | 20 | 6 | 12 | 72 |
|
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"row(*pl.align_frames(*dfs, on=['x', 'y']))"
]
},
{
"cell_type": "markdown",
"id": "e6a02173-afa4-45cf-bfc8-683c8dc59f50",
"metadata": {},
"source": [
"## join"
]
},
{
"cell_type": "markdown",
"id": "d3bc78b0-0112-4930-a3d8-f2ba6430fed9",
"metadata": {},
"source": [
"`Polars`の`join()`メソッドは、SQLのように2つのデータフレームを結合するための方法を提供します。`join`は、異なる結合戦略を使用して、2つのデータフレームの対応する行をマッチさせることができます。\n",
"\n",
"```python\n",
"df.join(\n",
" other, # 結合するもう1つのDataFrame\n",
" on=None, # 両方のDataFrameの結合に使う列名または式\n",
" how='inner', # 結合方法(デフォルトは'inner')\n",
" left_on=None, # 左側のDataFrameの結合列\n",
" right_on=None, # 右側のDataFrameの結合列\n",
" suffix='_right', # 重複した列名に付ける接尾辞\n",
" validate='m:m', # 結合タイプの検証 ('m:m', 'm:1', '1:m', '1:1')\n",
" join_nulls=False, # Null値もマッチさせるかどうか\n",
" coalesce=None # 共通のキー列に対してnull値を埋めるかどうか\n",
")\n",
"```\n",
"\n",
"引数`how`で結合方法を指定します。\n",
"\n",
"- **inner**: 両方のテーブルで一致する行を返す。\n",
"- **left**: 左のテーブルのすべての行と、右のテーブルの一致する行を返す。\n",
"- **right**: 右のテーブルのすべての行と、左のテーブルの一致する行を返す。\n",
"- **full**: 左右どちらかに一致する行をすべて返す。\n",
"- **semi**: 左テーブルから一致する行を返すが、右のテーブルからは列を返さない。\n",
"- **anti**: 左テーブルの一致しない行を返す。"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "02e6395e-ff2c-47c6-9fa3-dc3985d5e08e",
"metadata": {},
"outputs": [],
"source": [
"df_left = pl.DataFrame({\n",
" \"id\": [1, 2, 3, 4],\n",
" \"name\": [\"Alice\", \"Bob\", \"Charlie\", \"David\"]\n",
"})\n",
"\n",
"df_right = pl.DataFrame({\n",
" \"id\": [3, 4, 5],\n",
" \"age\": [23, 30, 40]\n",
"})"
]
},
{
"cell_type": "markdown",
"id": "af92bd41-9a8b-47c6-b4f7-fd4375da63e8",
"metadata": {},
"source": [
"### inner\n",
"\n",
"両方のデータフレームに存在する`id`に基づいて、内部結合を行います。次の例では、`id`が3と4に一致する行のみが返されました。"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "57a07cba-5ad4-4128-ba60-15432664bd4d",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" shape: (4, 2)id | name |
---|
i64 | str | 1 | "Alice" | 2 | "Bob" | 3 | "Charlie" | 4 | "David" |
| | \n",
" shape: (2, 3)id | name | age |
---|
i64 | str | i64 | 3 | "Charlie" | 23 | 4 | "David" | 30 |
|
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"row(\n",
" df_left, df_right, \n",
" df_left.join(df_right, on=\"id\", how=\"inner\") \n",
")"
]
},
{
"cell_type": "markdown",
"id": "df0ddeae-5c02-4061-a236-8e1fd00775ba",
"metadata": {},
"source": [
"### leftとright\n",
"\n",
"左のデータフレームのすべての行を返し、右のデータフレームに一致するデータがあれば、それも含めます。次の例では、`id`が1と2の行は右に対応するデータがないため、`age`は`null`です。"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "caa7b17e-73c9-446f-9ad0-6615f1dc8a00",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" shape: (4, 2)id | name |
---|
i64 | str | 1 | "Alice" | 2 | "Bob" | 3 | "Charlie" | 4 | "David" |
| | \n",
" shape: (4, 3)id | name | age |
---|
i64 | str | i64 | 1 | "Alice" | null | 2 | "Bob" | null | 3 | "Charlie" | 23 | 4 | "David" | 30 |
|
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"row(\n",
" df_left, df_right,\n",
" df_left.join(df_right, on=\"id\", how=\"left\")\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "d118ddb3-6071-4028-9d69-892f73064027",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" shape: (4, 2)id | name |
---|
i64 | str | 1 | "Alice" | 2 | "Bob" | 3 | "Charlie" | 4 | "David" |
| | \n",
" shape: (3, 3)name | id | age |
---|
str | i64 | i64 | "Charlie" | 3 | 23 | "David" | 4 | 30 | null | 5 | 40 |
|
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"row(\n",
" df_left, df_right,\n",
" df_left.join(df_right, on=\"id\", how=\"right\")\n",
")"
]
},
{
"cell_type": "markdown",
"id": "f7af0d6d-0804-4b48-bba3-722889e56248",
"metadata": {},
"source": [
"### full\n",
"\n",
"両方のデータフレームのすべての行を返し、どちらかに存在するデータがあれば、それを含めます。左と右のどちらからデータを取得したかを区別するために、結果には二つの結合列が作成されます。右側の結合列には、重複を避けるために`_right`という接尾辞が追加されます。結果から、idが1と2の行は左側のデータにのみ存在し、idが5の行は右側のデータにのみ存在することがわかります。"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "8fd7a8b0-c9bf-41a5-9286-79d91be0fd76",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" shape: (4, 2)id | name |
---|
i64 | str | 1 | "Alice" | 2 | "Bob" | 3 | "Charlie" | 4 | "David" |
| | \n",
" shape: (5, 4)id | name | id_right | age |
---|
i64 | str | i64 | i64 | 1 | "Alice" | null | null | 2 | "Bob" | null | null | 3 | "Charlie" | 3 | 23 | 4 | "David" | 4 | 30 | null | null | 5 | 40 |
|
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"row(\n",
" df_left, df_right,\n",
" df_left.join(df_right, on=\"id\", how=\"full\")\n",
")"
]
},
{
"cell_type": "markdown",
"id": "9e3612fa-4f9d-4b46-88b8-15af5d7591be",
"metadata": {},
"source": [
"`coalesce`引数を`True`に設定すると、これらの2つの列は1つにまとめられます。"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "03b5626b-f82d-4812-9b6a-4feb9e038f26",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" shape: (4, 2)id | name |
---|
i64 | str | 1 | "Alice" | 2 | "Bob" | 3 | "Charlie" | 4 | "David" |
| | \n",
" shape: (5, 3)id | name | age |
---|
i64 | str | i64 | 1 | "Alice" | null | 2 | "Bob" | null | 3 | "Charlie" | 23 | 4 | "David" | 30 | 5 | null | 40 |
|
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"row(\n",
" df_left, df_right,\n",
" df_left.join(df_right, on=\"id\", how=\"full\", coalesce=True)\n",
")"
]
},
{
"cell_type": "markdown",
"id": "f719e647-9b30-472d-932c-ea05ee100ffa",
"metadata": {},
"source": [
"### semiとanti"
]
},
{
"cell_type": "markdown",
"id": "66151d0f-5886-43ed-a3cd-7f1e9ce6a02d",
"metadata": {},
"source": [
"`semi`は右側に存在する行を出力します。`anti`は右側に存在しない行を出力します。semiとantiの結果には、右側の列は含まれません。"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "7e5f9a3d-ff20-4768-904c-184507235be3",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" shape: (4, 2)id | name |
---|
i64 | str | 1 | "Alice" | 2 | "Bob" | 3 | "Charlie" | 4 | "David" |
| | \n",
" shape: (2, 2)id | name |
---|
i64 | str | 3 | "Charlie" | 4 | "David" |
|
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"row(\n",
" df_left, df_right,\n",
" df_left.join(df_right, on=\"id\", how=\"semi\")\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "bf9d7204-f0ac-4c23-9fc6-dfbaa46bb005",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" shape: (4, 2)id | name |
---|
i64 | str | 1 | "Alice" | 2 | "Bob" | 3 | "Charlie" | 4 | "David" |
| | \n",
" shape: (2, 2)id | name |
---|
i64 | str | 1 | "Alice" | 2 | "Bob" |
|
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"row(\n",
" df_left, df_right,\n",
" df_left.join(df_right, on=\"id\", how=\"anti\")\n",
")"
]
},
{
"cell_type": "markdown",
"id": "6f052957-f213-4fea-939d-3a53cc395473",
"metadata": {},
"source": [
"### cross"
]
},
{
"cell_type": "markdown",
"id": "519a3f42-fddf-4969-9017-14cc3309541c",
"metadata": {},
"source": [
"`cross`は、2つのデータフレームのデカルト積を出力します。つまり、左側のすべての行と右側のすべての行の組み合わせを結合します。この場合は、`on`引数の指定は不要になります。"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "cdbf69d0-20d1-452e-b06c-a71e4cb5f41e",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" shape: (4, 2)id | name |
---|
i64 | str | 1 | "Alice" | 2 | "Bob" | 3 | "Charlie" | 4 | "David" |
| | \n",
" shape: (12, 4)id | name | id_right | age |
---|
i64 | str | i64 | i64 | 1 | "Alice" | 3 | 23 | 1 | "Alice" | 4 | 30 | 1 | "Alice" | 5 | 40 | 2 | "Bob" | 3 | 23 | 2 | "Bob" | 4 | 30 | … | … | … | … | 3 | "Charlie" | 4 | 30 | 3 | "Charlie" | 5 | 40 | 4 | "David" | 3 | 23 | 4 | "David" | 4 | 30 | 4 | "David" | 5 | 40 |
|
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"row(\n",
" df_left, df_right,\n",
" df_left.join(df_right, how=\"cross\")\n",
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "b6e1a35f-a9ef-4d58-9712-5bf273b14eec",
"metadata": {},
"source": [
"### validate\n",
"\n",
"`join` を使ってデータフレームを結合する際、結合キーに対応する左側と右側のデータ行数によって、次の 4 つのパターンが考えられます。 \n",
"\n",
"* **1:1(一対一)**: 左右の結合キーが **どちらもユニーク** である必要があります。(例: 社員 ID と 社員証番号) \n",
"* **1:m(一対多)**: 左のキーは **ユニーク** だが、右のキーは **重複可**。(例: 部署と所属社員) \n",
"* **m:1(多対一)**: 左のキーは **重複可** だが、右のキーは **ユニーク**。(例: 注文明細と商品マスタ) \n",
"* **m:m(多対多)**: 両方のキーに **重複がある** ことを許容。(デフォルトの動作) \n",
"\n",
"マッチングする行が複数ある場合、デフォルトでは **マッチしたすべての組み合わせ(デカルト積)** が結果に含まれます。 \n",
"\n",
"`join()` の `validate` 引数を使用すると、指定したマッチング関係を制約できます。設定した関係に違反する場合はエラーが発生します。 \n",
"\n",
"次の 2 つのデータフレームには、`id == 3` の行がそれぞれ 2 つずつ含まれています。 "
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "31545199-4cf2-4593-9db9-a2194583c7c8",
"metadata": {},
"outputs": [],
"source": [
"df_left = pl.DataFrame({\n",
" \"id\": [1, 2, 3, 4, 3],\n",
" \"name\": [\"Alice\", \"Bob\", \"Charlie\", \"David\", \"charlie\"]\n",
"})\n",
"\n",
"df_right = pl.DataFrame({\n",
" \"id\": [3, 4, 5, 3],\n",
" \"age\": [23, 30, 40, 46]\n",
"})"
]
},
{
"cell_type": "markdown",
"id": "e561e9c7-d71f-46b0-aec1-46ebc4911d50",
"metadata": {},
"source": [
"このまま `id` をキーにして `join()` を実行すると、`id == 3` の行は **デカルト積** となり、結果には 4 行が生成されます。 "
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "8d09c1a9-85f1-43cb-a796-f4add93f0826",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"
shape: (5, 3)id | name | age |
---|
i64 | str | i64 |
3 | "Charlie" | 23 |
3 | "Charlie" | 46 |
4 | "David" | 30 |
3 | "charlie" | 23 |
3 | "charlie" | 46 |
"
],
"text/plain": [
"shape: (5, 3)\n",
"┌─────┬─────────┬─────┐\n",
"│ id ┆ name ┆ age │\n",
"│ --- ┆ --- ┆ --- │\n",
"│ i64 ┆ str ┆ i64 │\n",
"╞═════╪═════════╪═════╡\n",
"│ 3 ┆ Charlie ┆ 23 │\n",
"│ 3 ┆ Charlie ┆ 46 │\n",
"│ 4 ┆ David ┆ 30 │\n",
"│ 3 ┆ charlie ┆ 23 │\n",
"│ 3 ┆ charlie ┆ 46 │\n",
"└─────┴─────────┴─────┘"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df_left.join(df_right, on=\"id\")"
]
},
{
"cell_type": "markdown",
"id": "67306ac0-1944-40d3-b981-ec16639b56e8",
"metadata": {},
"source": [
"`validate=\"1:1\"` を指定すると、結合キーに対して **一対一の関係** が満たされない場合にエラーとなり、データの整合性をチェックできます。 "
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "3327214a-8acd-4be1-b4b5-dd962d02098d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ComputeError: join keys did not fulfill 1:1 validation\n"
]
}
],
"source": [
"%%capture_except\n",
"df_left.join(df_right, on=\"id\", validate='1:1')"
]
},
{
"cell_type": "markdown",
"id": "31ad8a2a-ebb0-4613-92db-43bf911c99b6",
"metadata": {},
"source": [
"## update\n",
"\n",
"`DataFrame.update`メソッドは、Polarsにおいてインデックス列を基に既存のデータフレームを更新するために使用されます。引数`on`で指定された列が一致する行に対して、他の列のデータを更新します。\n",
"\n",
"以下のコードは、`df`と`df_update`の`id`列が一致する行を基に、`df_update`の他の列(`x`と`y`)の値を`df`に更新する例です。"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "6fb9ae91-3d25-4cec-9c08-2f9212c2826c",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" shape: (4, 3)id | x | y |
---|
i64 | i64 | i64 | 1 | 10 | 40 | 2 | 20 | 30 | 3 | 30 | 20 | 4 | 40 | 10 |
| \n",
" shape: (2, 3)id | x | y |
---|
i64 | i64 | i64 | 1 | 100 | -10 | 3 | 300 | -20 |
| \n",
" shape: (4, 3)id | x | y |
---|
i64 | i64 | i64 | 1 | 100 | -10 | 2 | 20 | 30 | 3 | 300 | -20 | 4 | 40 | 10 |
|
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"df = pl.DataFrame({\n",
" \"id\": [1, 2, 3, 4],\n",
" \"x\": [10, 20, 30, 40],\n",
" \"y\": [40, 30, 20, 10]\n",
"})\n",
"\n",
"df_update = pl.DataFrame({\n",
" \"id\": [1, 3],\n",
" \"x\": [100, 300],\n",
" \"y\": [-10, -20],\n",
"})\n",
"\n",
"df_res = df.update(df_update, on='id')\n",
"row(df, df_update, df_res)"
]
},
{
"cell_type": "markdown",
"id": "597a4e43-1a0d-45ca-8d77-36ce223e63d9",
"metadata": {},
"source": [
"## join_asof"
]
},
{
"cell_type": "markdown",
"id": "a9b3a06a-41bc-45dc-8132-7a388c4be83f",
"metadata": {},
"source": [
"`join_asof`は、時間や数値のような連続的なデータに基づいて2つのDataFrameを「概ね一致」させて結合するメソッドです。これは、正確な一致ではなく、片方の値がもう片方の値の近くにある場合に使われます。主に、**時系列データ**のような順序のあるデータで利用されます。\n",
"\n",
"`join_asof`は通常、次のような状況で使われます:\n",
"- 片方のデータが特定の時間に対するスナップショットを持ち、もう片方がその時間に最も近い値を持っている場合。\n",
"- \"前方一致\"または\"後方一致\"など、指定された方向に最も近いデータを探す場合。"
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "ce6f848c-21a1-4ed9-a595-22791466de67",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" shape: (5, 2)time | event |
---|
i64 | str | 1 | "A" | 5 | "B" | 10 | "C" | 15 | "D" | 20 | "E" |
| \n",
" shape: (4, 2)time | price |
---|
i64 | i64 | 2 | 100 | 6 | 105 | 12 | 110 | 18 | 115 |
| \n",
" shape: (5, 3)time | event | price |
---|
i64 | str | i64 | 1 | "A" | null | 5 | "B" | 100 | 10 | "C" | 105 | 15 | "D" | 110 | 20 | "E" | 115 |
|
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import polars as pl\n",
"\n",
"df1 = pl.DataFrame(\n",
" {\n",
" \"time\": [1, 5, 10, 15, 20],\n",
" \"event\": [\"A\", \"B\", \"C\", \"D\", \"E\"],\n",
" }\n",
")\n",
"\n",
"df2 = pl.DataFrame(\n",
" {\n",
" \"time\": [2, 6, 12, 18],\n",
" \"price\": [100, 105, 110, 115],\n",
" }\n",
")\n",
"\n",
"result = df1.join_asof(df2, on=\"time\", strategy=\"backward\")\n",
"row(df1, df2, result)"
]
},
{
"cell_type": "markdown",
"id": "b320bf0e-4fe8-4f28-9c4a-286799b2a96e",
"metadata": {},
"source": [
"`df1`の各行に対して、`df2`の`\"time\"`列で最も近くて「過去または現在の時間」にあたる行を結合します。つまり、`df1`の各行に対して、`df2`でその`\"time\"`に一番近い過去の`\"price\"`の値を結合します。`strategy=\"backward\"`は、`df1`の`\"time\"`の値に対して、それよりも過去または同時刻の`df2`の値を選ぶという戦略です。もう一つのオプションに`\"forward\"`があり、これは未来の値を選択します。"
]
},
{
"cell_type": "markdown",
"id": "3de94b61-53c7-4d2f-8060-e485cfb2c312",
"metadata": {},
"source": [
"## join_where"
]
},
{
"cell_type": "markdown",
"id": "693bd26b-88c5-40f0-9b81-f1153fd7421f",
"metadata": {},
"source": [
"`join_where`では二つのDataFrameの列同士の比較条件を指定して、それに基づいて結合を行います。例えば、以下のコードでは、`df1`のtime列の値が、`df2`のtime_span列に含まれる時間範囲内にある場合に結合が行われます。複数の条件式がある場合は、それらすべての条件式を満たす場合にのみ結合が行われます。"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "f9b8d5b0-c2f7-4f69-8707-d6b4e9c1f4f5",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" shape: (3, 2)id | time |
---|
i64 | i64 | 100 | 120 | 101 | 140 | 102 | 160 |
| \n",
" shape: (4, 2)t_id | time_span |
---|
i64 | list[i64] | 404 | [100, 110] | 498 | [110, 130] | 676 | [90, 100] | 742 | [150, 170] |
| \n",
" shape: (2, 4)id | time | t_id | time_span |
---|
i64 | i64 | i64 | list[i64] | 100 | 120 | 498 | [110, 130] | 102 | 160 | 742 | [150, 170] |
|
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"df1 = pl.DataFrame(\n",
" {\n",
" \"id\": [100, 101, 102],\n",
" \"time\": [120, 140, 160],\n",
" }\n",
")\n",
"df2 = pl.DataFrame(\n",
" {\n",
" \"t_id\": [404, 498, 676, 742],\n",
" \"time_span\": [(100, 110), (110, 130), (90, 100), (150, 170)],\n",
" }\n",
")\n",
"df_res = df1.join_where(\n",
" df2,\n",
" pl.col('time') >= pl.col('time_span').list.get(0),\n",
" pl.col('time') <= pl.col('time_span').list.get(1)\n",
")\n",
"row(df1, df2, df_res)"
]
},
{
"cell_type": "markdown",
"id": "4ced7fae-c536-4d9c-97d5-4251ef6720f3",
"metadata": {},
"source": [
"`.join_where()`はブール演算や`is_between()`もサポートしています。"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "3993378b-e4a0-416d-b51b-1da3f4495762",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" shape: (2, 4)id | time | t_id | time_span |
---|
i64 | i64 | i64 | list[i64] | 100 | 120 | 498 | [110, 130] | 102 | 160 | 742 | [150, 170] |
| \n",
" shape: (2, 4)id | time | t_id | time_span |
---|
i64 | i64 | i64 | list[i64] | 100 | 120 | 498 | [110, 130] | 102 | 160 | 742 | [150, 170] |
|
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"df_res1 = df1.join_where(\n",
" df2,\n",
" (pl.col('time') >= pl.col('time_span').list.get(0)) & (pl.col('time') <= pl.col('time_span').list.get(1))\n",
")\n",
"\n",
"df_res2 = df1.join_where(\n",
" df2,\n",
" pl.col('time').is_between(pl.col('time_span').list.get(0), pl.col('time_span').list.get(1))\n",
")\n",
"row(df_res1, df_res2)"
]
},
{
"cell_type": "markdown",
"id": "1950c6c0-0089-4277-85d4-0fd26f012518",
"metadata": {},
"source": [
"`.join_where()`内部では、二つのデータフレームの行のデカルト積を計算し、その中から条件を満たす行だけをフィルタリングして出力します。そのため、データの件数が多い場合、処理が非常に遅くなる可能性があります。例えば、次のコードでは、二つの点群間で距離が0.01より小さい点のペアを計算します。この処理は計算量が $N^2$ に比例するため、大きなデータセットでは速度が非常に遅くなります。"
]
},
{
"cell_type": "code",
"execution_count": 87,
"id": "8356e387-7e02-4c21-93ce-3f2e0844e139",
"metadata": {},
"outputs": [],
"source": [
"n = 4000\n",
"np.random.seed(42)\n",
"df1 = pl.DataFrame(dict(\n",
" x1=np.random.normal(size=n),\n",
" y1=np.random.normal(size=n),\n",
"))\n",
"df2 = pl.DataFrame(dict(\n",
" x2=np.random.normal(size=n),\n",
" y2=np.random.normal(size=n),\n",
"))"
]
},
{
"cell_type": "code",
"execution_count": 88,
"id": "99a510fa-4b1c-4695-88f8-15e738c41bf6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: total: 891 ms\n",
"Wall time: 435 ms\n"
]
}
],
"source": [
"%%time\n",
"df_res1 = (\n",
" df1\n",
" .join_where(\n",
" df2, \n",
" (pl.col('x2') - pl.col('x1'))**2 + (pl.col('y2') - pl.col('y1'))**2 < 0.0001\n",
" )\n",
" .sort('x1', 'y1')\n",
")"
]
},
{
"cell_type": "markdown",
"id": "340a5664-68e4-4d7d-a7cc-e68da0cdb0b3",
"metadata": {},
"source": [
"SciPyの`KDTree`を使用することで、このような処理を高速化できます。`KDTree`を利用すると、空間的な距離を効率的に計算し、特定の距離内にある点のペアを高速に見つけることができます。以下のコードでは、`tree1.query_ball_tree(tree2, 0.01)`を使用して範囲内の点のペアを計算し、結果を基に`df_pair`を作成します。その後、行番号をキーとして`df1`、`df_pair`、`df2`を結合して結果データフレーム`df_res2`を作成します。"
]
},
{
"cell_type": "code",
"execution_count": 89,
"id": "7272eb38-a937-42ba-83e4-e0f3cd69c3a0",
"metadata": {},
"outputs": [],
"source": [
"from scipy.spatial import KDTree"
]
},
{
"cell_type": "code",
"execution_count": 101,
"id": "15e0e248-d1b6-4c2e-aa62-65de32f86ace",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: total: 31.2 ms\n",
"Wall time: 19 ms\n"
]
}
],
"source": [
"%%time\n",
"tree1 = KDTree(df1.to_numpy())\n",
"tree2 = KDTree(df2.to_numpy())\n",
"res = tree1.query_ball_tree(tree2, 0.01)\n",
"\n",
"df_pair = (\n",
" pl.DataFrame(pl.Series('index2', res, dtype=pl.List(pl.UInt32))) # resをデータフレームに変換\n",
" .with_row_index('index1')\n",
" .filter(pl.col('index2').list.len() > 0) # 範囲内の点が存在する行のみを残す\n",
" .explode('index2') # リストを展開して1行1ペアにする\n",
")\n",
"df_res2 = (\n",
" df1\n",
" .with_row_index('index1')\n",
" .join(df_pair, on='index1')\n",
" .join(\n",
" df2.with_row_index('index2'), on='index2')\n",
" .drop('index1', 'index2')\n",
" .sort('x1', 'y1')\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 94,
"id": "7dc153cc-bd03-41df-9427-63fa27913fa0",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" shape: (420, 4)x1 | y1 | x2 | y2 |
---|
f64 | f64 | f64 | f64 | -2.065083 | -0.70744 | -2.05916 | -0.70436 | -1.769076 | 0.817306 | -1.768415 | 0.825853 | -1.523187 | -0.501234 | -1.514414 | -0.501647 | -1.519346 | -0.035096 | -1.527461 | -0.03116 | -1.478522 | 0.932435 | -1.482397 | 0.94037 | … | … | … | … | 1.66907 | 0.388819 | 1.678574 | 0.385821 | 1.696456 | -0.038743 | 1.692313 | -0.037887 | 1.734937 | 0.142991 | 1.737985 | 0.135518 | 1.831459 | -0.532725 | 1.831177 | -0.523104 | 1.954157 | 0.568273 | 1.948963 | 0.563404 |
| \n",
" shape: (420, 2)index1 | index2 |
---|
u32 | u32 | 10 | 1832 | 10 | 2789 | 41 | 1502 | 44 | 739 | 45 | 2018 | … | … | 3931 | 1196 | 3935 | 1035 | 3964 | 1214 | 3978 | 715 | 3994 | 1588 |
| \n",
" shape: (420, 4)x1 | y1 | x2 | y2 |
---|
f64 | f64 | f64 | f64 | -2.065083 | -0.70744 | -2.05916 | -0.70436 | -1.769076 | 0.817306 | -1.768415 | 0.825853 | -1.523187 | -0.501234 | -1.514414 | -0.501647 | -1.519346 | -0.035096 | -1.527461 | -0.03116 | -1.478522 | 0.932435 | -1.482397 | 0.94037 | … | … | … | … | 1.66907 | 0.388819 | 1.678574 | 0.385821 | 1.696456 | -0.038743 | 1.692313 | -0.037887 | 1.734937 | 0.142991 | 1.737985 | 0.135518 | 1.831459 | -0.532725 | 1.831177 | -0.523104 | 1.954157 | 0.568273 | 1.948963 | 0.563404 |
|
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"row(df_res1, df_pair, df_res2)"
]
},
{
"cell_type": "markdown",
"id": "aaba9a07-b00b-4696-9f72-2b0a70651f9f",
"metadata": {},
"source": [
"`res`は二重リストで構成されており、各要素は`tree1`内の各点に対応しています。それぞれの要素には、対応する`tree2`内で指定範囲内にある点のインデックス(番号)のリストが格納されています。次のコードは、`tree1`の先頭50点について計算結果を表示し、空リスト(範囲内に点がない場合)をスキップします。\n",
"\n",
"この出力から、`tree1`の点10は、`tree2`の点1832と2789が距離0.01以内に存在することが分かります。"
]
},
{
"cell_type": "code",
"execution_count": 102,
"id": "a64aa210-515b-493f-b8d5-0e5bfdb64a4f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10 [1832, 2789]\n",
"41 [1502]\n",
"44 [739]\n",
"45 [2018]\n"
]
}
],
"source": [
"for i, index2 in enumerate(res[:50]):\n",
" if index2:\n",
" print(i, index2)"
]
},
{
"cell_type": "markdown",
"id": "4176b902-fb0d-44eb-a194-edb231a9b92a",
"metadata": {},
"source": [
"## extendとvstackメソッド"
]
},
{
"cell_type": "markdown",
"id": "2fb4a09d-8c03-4a0a-9f47-3089ee98242b",
"metadata": {},
"source": [
"`DataFrame.extend()` と `DataFrame.vstack()` は、既存のデータフレームに別のデータフレームの内容を行として追加するメソッドです。\n",
"\n",
"- **`extend()`**: 元のデータフレームを直接変更します(その場で変更)。変更後のデータフレームは返り値として返します。\n",
"- **`vstack()`**: `in_place` 引数を指定することで、元のデータフレームを直接変更する(その場で変更)か、新しいデータフレームを返すかを選べます。"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "a4aa28d7-4159-4eb9-8472-33f34d8fa035",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"df1 = pl.DataFrame({\"a\": [1, 2], \"b\": [3, 4]})\n",
"df2 = pl.DataFrame({\"a\": [5, 6], \"b\": [7, 8]})\n",
"\n",
"# clone()を使って元のデータフレームを複製\n",
"df_extend = df1.clone()\n",
"df_extend.extend(df2) # extendで直接変更\n",
"\n",
"df_vstack = df1.clone()\n",
"df_vstack.vstack(df2, in_place=True) # vstackでその場で変更\n",
"row(df_extend, df_vstack)"
]
},
{
"cell_type": "markdown",
"id": "3b52fa63-dbaf-413a-aa49-d27e9bcbf0cb",
"metadata": {},
"source": [
"両者の違いは、メモリ管理の方法にあります。\n",
"\n",
"`extend()` は、引数のデータフレームの内容を元のデータフレームのメモリの末尾にコピーします。結果として、結合後のデータフレームのメモリは連続しています。ただし、元のデータフレームのメモリが再配置される可能性があるため、ループ内で頻繁に `extend()` を使用することは推奨されません。\n",
"\n",
"`vstack()` は、結合する2つのデータフレームのメモリをそのままにし、リンクによって両者を結合します。そのため、結果として得られるデータフレームのメモリは不連続になります。この不連続性により、後で計算を行う際のパフォーマンスが低下する可能性があります。 \n",
"一般的には、ループ内で `vstack()` を使用してデータを結合し、最後に `rechunk()` を実行してメモリを連続した状態に変換するのが効率的です。"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "90502bf1-1e75-45ac-8901-e138cc8410df",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"df_extend.n_chunks() = 1\n",
"df_vstack.n_chunks() = 2\n",
"df_vstack.rechunk().n_chunks() = 1\n"
]
}
],
"source": [
"print(f'{df_extend.n_chunks() = }') # extend後のチャンク数\n",
"print(f'{df_vstack.n_chunks() = }') # vstack後のチャンク数\n",
"print(f'{df_vstack.rechunk().n_chunks() = }') # rechunk後のチャンク数"
]
},
{
"cell_type": "markdown",
"id": "f2629a50-d836-47cd-acfa-0cf9ed1f8b79",
"metadata": {},
"source": [
"## hstackメソッド"
]
},
{
"cell_type": "markdown",
"id": "70a299ff-d4bd-426f-9ff8-fa15c25a3f33",
"metadata": {},
"source": [
"`DataFrame.hstack()` は、2つのデータフレームを水平方向(列方向)に結合します。`in_place=True` を指定すると、元のデータフレームに列を追加します。"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "b8b59f9c-5140-475e-af33-b8e4fcc63b49",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"df1 = pl.DataFrame({\"a\": [1, 2], \"b\": [3, 4]})\n",
"df2 = pl.DataFrame({\"c\": [5, 6], \"d\": [7, 8]})\n",
"\n",
"# in-place操作なし(新しいデータフレームを返す)\n",
"df3 = df1.hstack(df2)\n",
"\n",
"# in-place操作あり\n",
"df1.hstack(df2, in_place=True)\n",
"row(df3, df1)"
]
},
{
"cell_type": "markdown",
"id": "ae632a19-f9b4-45f5-b161-4ab9e5995e4b",
"metadata": {},
"source": [
"## merge_sortedメソッド"
]
},
{
"cell_type": "markdown",
"id": "9e4a7d62-23ac-4d5d-9217-37d8f7e209a7",
"metadata": {},
"source": [
"`DataFrame.merge_sorted()` は、**あらかじめソートされた** 2つのデータフレームを縦方向に結合し、指定したキー列に基づいてソートされた新しいデータフレームを返すメソッドです。このメソッドを使用する際は、入力となる両方のデータフレームが事前に指定したキー列でソートされていることが前提となります。"
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "03594f09-9199-448b-b418-69c7b79fd117",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"df1 = pl.DataFrame({\"a\": [3, 1, 5], \"b\": [6, 4, 2]})\n",
"df2 = pl.DataFrame({\"a\": [6, 2, 4], \"b\": [7, 5, 3]})\n",
"\n",
"# \"a\" 列でソートしてからマージ\n",
"df3 = df1.sort(\"a\").merge_sorted(df2.sort(\"a\"), key=\"a\")\n",
"\n",
"# \"b\" 列でソートしてからマージ\n",
"df4 = df1.sort(\"b\").merge_sorted(df2.sort(\"b\"), key=\"b\")\n",
"\n",
"row(df3, df4)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}