詳解Polars 多 DataFrame 合并操作全指南
從 pandas 遷移過(guò)來(lái)的人,最常問(wèn)的一句話是:"concat 和 merge 在 Polars 里怎么寫(xiě)?"——答案不只是"有替代品",而是"比你想象的更完整"。
一、pl.concat()—— 拼接這件事,Polars 想得很周全
pandas 的 concat 能做的,Polars 全都有;pandas 做不到的,Polars 也補(bǔ)上了。核心函數(shù)是 pl.concat(),通過(guò)一個(gè) how 參數(shù)切換行為,覆蓋了從最簡(jiǎn)單的縱向堆疊到多表對(duì)齊合并的幾乎所有場(chǎng)景。
函數(shù)簽名
pl.concat(
items: Iterable[DataFrame | LazyFrame | Series],
*,
how: str = 'vertical',
rechunk: bool = False,
parallel: bool = True,
)
??vertical— 最樸素的縱向堆疊
列名和類(lèi)型必須完全一致,行數(shù)疊加變長(zhǎng)。這是默認(rèn)模式,也是日常用得最多的。
import polars as pl
df1 = pl.DataFrame({"a": [1], "b": [3]})
df2 = pl.DataFrame({"a": [2], "b": [4]})
result = pl.concat([df1, df2])
# shape: (2, 2)
# │ a ┆ b │
# │ 1 ┆ 3 │
# │ 2 ┆ 4 │
列名對(duì)不上會(huì)直接報(bào)錯(cuò),沒(méi)有靜默處理。
??vertical_relaxed— 類(lèi)型不完全一致?自動(dòng)升檔
和 vertical 邏輯相同,但遇到類(lèi)型沖突時(shí)會(huì)自動(dòng)提升為公共超類(lèi)型,比如 Int32 遇上 Float64,結(jié)果列統(tǒng)一變成 Float64,省去手動(dòng)轉(zhuǎn)換的麻煩。
df1 = pl.DataFrame({"a": [1], "b": [3]})
df2 = pl.DataFrame({"a": [2.5], "b": [4]})
result = pl.concat([df1, df2], how="vertical_relaxed")
# 列 a 自動(dòng)從 i64 提升為 f64
??horizontal— 橫著拼,變寬不變長(zhǎng)
把多個(gè) DataFrame 的列并排擺在一起,行數(shù)不同時(shí)短的那邊用 null 補(bǔ)齊。列名不能重疊,否則報(bào)錯(cuò)。
df_h1 = pl.DataFrame({"l1": [1, 2], "l2": [3, 4]})
df_h2 = pl.DataFrame({"r1": [5, 6, 7], "r2": [8, 9, 10]})
result = pl.concat([df_h1, df_h2], how="horizontal")
# shape: (3, 4),df_h1 的兩列用 null 補(bǔ)到第 3 行
??diagonal— 列結(jié)構(gòu)不同?取并集,缺的填 null
這是處理"列不對(duì)齊"場(chǎng)景最直接的方式。兩張表的列名取并集,各自沒(méi)有的列填 null,行數(shù)疊加。
df_d1 = pl.DataFrame({"a": [1], "b": [3]})
df_d2 = pl.DataFrame({"a": [2], "c": [4]})
result = pl.concat([df_d1, df_d2], how="diagonal")
# shape: (2, 3)
# │ a ┆ b ┆ c │
# │ 1 ┆ 3 ┆ null │
# │ 2 ┆ null ┆ 4 │
diagonal_relaxed 在此基礎(chǔ)上額外支持類(lèi)型自動(dòng)提升,兩者搭配基本能覆蓋絕大多數(shù)"臟數(shù)據(jù)合并"的場(chǎng)景。
??align系列 —— 多表按鍵對(duì)齊,一步到位 ?
這是 Polars 相對(duì)新的能力,也是最有意思的一個(gè)。它會(huì)自動(dòng)識(shí)別多個(gè) DataFrame 的公共鍵列,按鍵對(duì)齊后橫向合并——本質(zhì)上是幫你把"先 join 再 concat"這兩步合成了一步。
df_a1 = pl.DataFrame({"id": [1, 2], "x": [3, 4]})
df_a2 = pl.DataFrame({"id": [2, 3], "y": [5, 6]})
df_a3 = pl.DataFrame({"id": [1, 3], "z": [7, 8]})
pl.concat([df_a1, df_a2, df_a3], how="align")
# shape: (3, 4)
# │ id ┆ x ┆ y ┆ z │
# │ 1 ┆ 3 ┆ null ┆ 7 │
# │ 2 ┆ 4 ┆ 5 ┆ null │
# │ 3 ┆ null ┆ 6 ┆ 8 │
align 系列有四種變體,對(duì)應(yīng)不同的保留策略:
| how 參數(shù) | 等效 join 類(lèi)型 |
|---|---|
| align / align_full | full outer join |
| align_left | left join |
| align_right | right join |
| align_inner | inner join |
pl.concat()全策略速查
| how 參數(shù) | 行為 | pandas 對(duì)應(yīng) | 適用場(chǎng)景 |
|---|---|---|---|
| vertical | 縱向堆疊,列必須一致 | concat(axis=0) | 同結(jié)構(gòu)多批數(shù)據(jù)合并 |
| vertical_relaxed | 縱向堆疊,類(lèi)型自動(dòng)提升 | concat(axis=0) | 類(lèi)型略有差異的同結(jié)構(gòu)數(shù)據(jù) |
| horizontal | 橫向并列,列不可重疊 | concat(axis=1) | 不同特征列拼接 |
| diagonal | 取列并集,缺失填 null | concat(axis=0, join='outer') | 列結(jié)構(gòu)不同的數(shù)據(jù)合并 |
| diagonal_relaxed | 同上 + 類(lèi)型提升 | 同上 | 列結(jié)構(gòu)和類(lèi)型均不同 |
| align | 按公共鍵對(duì)齊橫向合并 | merge + concat | 多 df 按 key 對(duì)齊 |
二、df.join()—— 關(guān)聯(lián)合并,SQL 那套邏輯全都在
如果說(shuō) concat 是"把表擺在一起",那 join 就是"按條件把兩張表的行配對(duì)"。Polars 的 join() 覆蓋了 SQL 里幾乎所有標(biāo)準(zhǔn) join 類(lèi)型,語(yǔ)法也比 pandas 的 merge 更簡(jiǎn)潔。
函數(shù)簽名
df.join(
other: DataFrame,
on: str | list | None = None,
how: str = 'inner',
*,
left_on=None,
right_on=None,
suffix: str = '_right',
validate: str = 'm:m',
join_nulls: bool = False,
coalesce: bool | None = None,
)
六種 join 策略,逐一拆解
inner join(默認(rèn)) — 只留兩邊都能匹配上的行,是最常見(jiàn)的用法。
df.join(other_df, on="ham")
left join — 左表全保留,右表沒(méi)匹配到的位置填 null。
df.join(other_df, on="ham", how="left", coalesce=True)
full join — 兩邊都全保留,互相沒(méi)匹配到的填 null。數(shù)據(jù)探查時(shí)很好用。
df.join(other_df, on="ham", how="full")
cross join — 笛卡爾積,不需要 on 參數(shù),生成所有行的兩兩組合。數(shù)據(jù)量小時(shí)偶爾有用,大表慎用。
df.join(other_df, how="cross")
semi join — 只保留左表中能在右表找到匹配的行,但不附加右表的列。本質(zhì)上是個(gè)過(guò)濾器,比先 join 再 select 列更高效。
df.join(other_df, on="ham", how="semi")
anti join — semi join 的反面,只保留左表中在右表找不到匹配的行。做數(shù)據(jù)差異比對(duì)時(shí)非常順手。
df.join(other_df, on="ham", how="anti")
幾個(gè)容易忽略的參數(shù)
| 參數(shù) | 說(shuō)明 |
|---|---|
| left_on / right_on | 兩表鍵列名不同時(shí)分別指定,不必提前重命名 |
| suffix | 重名列的后綴,默認(rèn) _right,可自定義 |
| validate | 校驗(yàn)關(guān)系類(lèi)型:'1:1'、'1:m'、'm:1'、'm:m',數(shù)據(jù)質(zhì)量把關(guān)利器 |
| join_nulls | 是否將 null 視為相等進(jìn)行匹配,默認(rèn) False |
| coalesce | 是否將兩邊的鍵列合并為一列輸出 |
validate 這個(gè)參數(shù)在生產(chǎn)環(huán)境里尤其值得打開(kāi)——它能在數(shù)據(jù)出現(xiàn)意外的一對(duì)多或多對(duì)多關(guān)系時(shí)直接報(bào)錯(cuò),而不是悄悄給你生成一張膨脹的結(jié)果表。
三、進(jìn)階武器:join_asof()與join_where()
join_asof()— 時(shí)序數(shù)據(jù)的專(zhuān)屬利器
時(shí)序場(chǎng)景里經(jīng)常遇到這樣的問(wèn)題:兩張表的時(shí)間戳對(duì)不上,但你想找"最近的那條記錄"。join_asof() 就是為此而生的,邏輯類(lèi)似 left join,但匹配的是最近的鍵值而非精確相等。兩表必須提前按鍵列排好序。
df.join_asof(
other,
on="timestamp",
strategy="backward", # 'backward'(默認(rèn)) | 'forward' | 'nearest'
tolerance="1d", # 超出容差范圍則不匹配
by="stock_id", # 先按此列分組,再做 asof join
)
三種匹配策略的含義直白明了:
- backward:找右表中 ≤ 左表時(shí)間戳的最近一行
- forward:找右表中 ≥ 左表時(shí)間戳的最近一行
- nearest:找距離最近的那行,不管方向
tolerance 支持?jǐn)?shù)值,也支持時(shí)間字符串,比如 "3d12h4m25s" 這樣的寫(xiě)法,處理金融或日志數(shù)據(jù)時(shí)相當(dāng)省心。
join_where()— 非等值條件匹配
標(biāo)準(zhǔn) join 只能做等值匹配,但現(xiàn)實(shí)中經(jīng)常需要"價(jià)格在某個(gè)區(qū)間內(nèi)""日期晚于某個(gè)時(shí)間點(diǎn)"這類(lèi)條件。join_where() 接受任意謂詞表達(dá)式,找出所有滿足條件的行對(duì)組合。
df.join_where(
other,
pl.col("price_left") >= pl.col("price_min"),
pl.col("price_left") <= pl.col("price_max"),
)
pandas 里沒(méi)有直接對(duì)應(yīng)的方法,通常要繞道 merge 加 query 或者手寫(xiě)循環(huán),Polars 把這個(gè)場(chǎng)景單獨(dú)封裝成一個(gè)函數(shù),思路很清晰。
四、Polars vs pandas 對(duì)照速查
從 pandas 遷移過(guò)來(lái),下面這張表應(yīng)該能省不少查文檔的時(shí)間:
| 需求場(chǎng)景 | pandas 寫(xiě)法 | Polars 寫(xiě)法 |
|---|---|---|
| 縱向堆疊(同結(jié)構(gòu)) | pd.concat([df1,df2]) | pl.concat([df1,df2]) |
| 縱向堆疊(列不同) | pd.concat([df1,df2], join='outer') | pl.concat([df1,df2], how="diagonal") |
| 橫向拼接 | pd.concat([df1,df2], axis=1) | pl.concat([df1,df2], how="horizontal") |
| inner join | pd.merge(df1,df2,on='key') | df1.join(df2, on='key') |
| left join | pd.merge(...,how='left') | df1.join(df2, on='key', how='left') |
| full outer join | pd.merge(...,how='outer') | df1.join(df2, on='key', how='full') |
| 時(shí)序近似匹配 | pd.merge_asof(...) | df1.join_asof(df2, on='ts') |
| 非等值條件匹配 | 無(wú)直接對(duì)應(yīng) | df1.join_where(df2, 條件表達(dá)式) |
五、選哪個(gè)?幾條實(shí)用判斷原則
多批同結(jié)構(gòu)數(shù)據(jù)合并,直接用 pl.concat([...]) 默認(rèn)的 vertical 模式,性能極高,沒(méi)有懸念。
列結(jié)構(gòu)不統(tǒng)一,用 diagonal 或 diagonal_relaxed,自動(dòng)補(bǔ) null,不用提前對(duì)齊列名。
按鍵關(guān)聯(lián)兩表,用 df.join(),記得打開(kāi) validate 參數(shù)做數(shù)據(jù)質(zhì)量校驗(yàn),生產(chǎn)環(huán)境里這一步能幫你擋掉很多隱患。
時(shí)序數(shù)據(jù)對(duì)齊,join_asof() 是專(zhuān)門(mén)為此設(shè)計(jì)的,配合 tolerance 參數(shù)避免匹配到過(guò)遠(yuǎn)的記錄。
LazyFrame 場(chǎng)景,pl.concat() 的 parallel=True 參數(shù)可以并行執(zhí)行多個(gè)懶計(jì)算,大數(shù)據(jù)量下性能優(yōu)勢(shì)會(huì)更明顯。
到此這篇關(guān)于詳解Polars 多 DataFrame 合并操作全指南的文章就介紹到這了,更多相關(guān)Polars 多 DataFrame 合并內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于keras中keras.layers.merge的用法說(shuō)明
這篇文章主要介紹了關(guān)于keras中keras.layers.merge的用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-05-05
Python合并2個(gè)字典成1個(gè)新字典的方法(9種)
這篇文章主要介紹了Python合并2個(gè)字典成1個(gè)新字典的方法,本文通過(guò)實(shí)例代碼給大家分享9中方法,需要的朋友可以參考下2019-12-12
Python+Paramiko編寫(xiě)簡(jiǎn)單的SFTP文件上傳下載工具類(lèi)
Paramiko 是 Python 中最流行的 SSH2 協(xié)議實(shí)現(xiàn)庫(kù),基于它我們可以輕松實(shí)現(xiàn) SFTP 客戶端功能,本文將分享一個(gè)封裝好的 SftpClient 類(lèi),支持文件上傳、下載,感興趣的小伙伴可以了解下2026-01-01
Python后臺(tái)管理員管理前臺(tái)會(huì)員信息的講解
今天小編就為大家分享一篇關(guān)于Python后臺(tái)管理員管理前臺(tái)會(huì)員信息的講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01
創(chuàng)建pycharm的自定義python模板方法
今天小編就為大家分享一篇?jiǎng)?chuàng)建pycharm的自定義python模板方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
Python實(shí)現(xiàn)Socket.IO的完整指南
Socket.IO?是一個(gè)基于?WebSocket?協(xié)議的實(shí)時(shí)雙向通信庫(kù),它為客戶端和服務(wù)器端提供了事件驅(qū)動(dòng)的接口,使開(kāi)發(fā)者能夠輕松實(shí)現(xiàn)實(shí)時(shí)消息的傳遞,本文給大家介紹了Python實(shí)現(xiàn)Socket.IO的完整指南,需要的朋友可以參考下2025-09-09
Python之Matplotlib繪圖調(diào)節(jié)清晰度解決方案
Matplotlib是一個(gè)Python的繪圖庫(kù),可以用來(lái)繪制各種類(lèi)型的圖表,包括線圖、散點(diǎn)圖、柱狀圖等等,這篇文章主要給大家介紹了關(guān)于Python之Matplotlib繪圖調(diào)節(jié)清晰度的相關(guān)資料,需要的朋友可以參考下2024-03-03

