pandas 左右邊界切割 用merge join和concat合併Pandas中的資料
阿新 • • 發佈:2020-09-06
熊貓Series和DataFrame物件是用於探索和分析資料的強大工具。它們的部分功能來自組合單獨資料集的多方面方法。使用Pandas,您可以合併,連線和連線資料集,從而在分析資料時可以統一併更好地理解資料。 在本教程中,您將學習如何以及何時在Pandas中結合以下資料: merge() 用於合併公共列或索引上的資料 .join() 用於組合鍵列或索引上的資料 concat() 用於跨行或跨列組合DataFrame 如果您具有在Pandas中使用DataFrame和Series物件的一些經驗,並且準備學習如何組合它們,那麼本教程將幫助您做到這一點。如果要在繼續操作之前對DataFrames進行快速重新整理,那麼Pandas DataFrames 101將使您立即趕上來。 您可以使用互動式Jupyter Notebook和下面連結中的資料檔案來跟隨本教程中的示例: 下載筆記本和資料集: 單擊此處以獲取Jupyter筆記本和CSV資料集,您將使用它們來學習本教程中的Pandas merge()、. join()和concat()。 注意:您將在下面學到的技術通常適用於DataFrame和Series物件。但是為了簡單明瞭,這些示例將使用術語資料集來指代可以是DataFrame或Series的物件。merge():在公共列的資料組合或指數 您將學習的第一種技術是merge()。您可以隨時使用merge()與資料庫類似的聯接操作。這是您將學習的三個操作中最靈活的。 如果要以與關係資料庫類似的方式基於一個或多個鍵來組合資料物件,merge()則需要使用此工具。更具體地說,merge()當您想要合併共享資料的行時,它最有用。 您可以使用實現多對一和多對多聯接merge()。在多對一聯接中,您的一個數據集的合併列中將有許多行重複相同的值(例如1、1、3、5、5),而另一個數據集中的合併列將不具有重複值(例如1、3、5)。 您可能已經猜到了,在多對多聯接中,兩個合併列都將具有重複值。這些合併更加複雜,並導致連線行的笛卡爾積。 這意味著,合併後,您將在鍵列中擁有共享相同值的行的每種組合。您將在以下示例中看到這一點。 是什麼讓merge()如此靈活是定義你的合併行為的選項數量之多。儘管列表看起來令人生畏,但通過實踐,您將能夠熟練地合併各種資料集。 使用時merge(),您將提供兩個必需的引數: 資料left框 資料right框 之後,您可以提供許多可選引數來定義如何合併資料集: how:這定義了進行哪種合併。它預設'inner',但其他可能的選項包括'outer','left',和'right'。 on:使用它可以告訴您要加入的merge()列或索引(也稱為鍵列或鍵索引)。這是可選的。如果未指定,並且left_index和right_index(在下面介紹)是False,則兩個名稱共享的DataFrame中的列將用作聯接鍵。如果使用on,則指定的列或索引必須同時存在於兩個物件中。 left_onandright_on:使用這兩個選項之一可以指定僅在您要合併的left或right物件中存在的列或索引。兩者都預設為None。 left_index和right_index:將它們設定True為使用要合併的左側或右側物件的索引。兩者均預設為False。 suffixes:這是要附加到不是合併鍵的相同列名稱的字串的元組。這使您可以跟蹤具有相同名稱的列的來源。 這些是傳遞給的一些最重要的引數merge()。有關完整列表,請參見Pandas文件。 注意:在本教程中,您將看到示例始終指定要與之連線的列on。這是合併資料的最安全方法,因為您和任何閱讀您程式碼的人都將確切知道merge()呼叫該呼叫時的期望。如果您未使用來指定合併列on,則Pandas將使用與合併鍵同名的任何列。 如何merge() 在深入瞭解如何使用之前merge(),您應該首先了解各種形式的聯接: inner outer left right 注意:即使你關於合併的學習,你會看到inner,outer,left,並且right也被稱為連線操作。對於本教程,您可以認為這些術語等效。 您將在下面詳細瞭解這些內容,但首先請看一下這些不同聯接的直觀表示: 連線操作的維恩圖 聯接型別的可視表示 在此影象中,兩個圓圈是您的兩個資料集,標籤指向您可以期望看到的資料集的一部分。儘管此圖未涵蓋所有細微差別,但對於視覺學習者而言,它可能是一個方便的指南。 如果您具有SQL背景,則可以從JOIN語法中識別合併操作名稱。除此之外inner,所有這些技術都是外部聯接的型別。使用外部聯接,您將基於左側物件,右側物件或兩者中的所有鍵合併資料。對於僅存在於一個物件中的鍵,另一物件中不匹配的列將用NaN(非數字)填充。 您還可以在Coding Horror的SQL上下文中看到各種聯接的直觀說明。現在讓我們看一下不同的聯接。 移除廣告 範例 許多Pandas教程都提供了非常簡單的DataFrame,以說明它們試圖解釋的概念。由於您無法將資料與任何具體內容相關聯,因此這種方法可能會造成混淆。因此,在本教程中,您將使用兩個真實的資料集作為要合併的DataFrame: 加利福尼亞的氣候常態(溫度) 加利福尼亞州的氣候正常值(降水) 您可以瀏覽這些資料集,並使用互動式Jupyter Notebook和氣候資料CSV遵循以下示例: 下載筆記本和資料集: 單擊此處以獲取Jupyter筆記本和CSV資料集,您將使用它們來學習本教程中的Pandas merge()、. join()和concat()。 如果您想學習如何使用Jupyter Notebook,請檢視Jupyter Notebook:簡介。 這兩個資料集來自美國國家海洋和大氣管理局(NOAA),並來自NOAA 公共資料儲存庫。首先,將資料集載入到單獨的DataFrame中:
import pandas as pd climate_temp = pd.read_csv("climate_temp.csv") climate_precip = pd.read_csv("climate_precip.csv")
在上面的程式碼中,您使用了Pandas read_csv()來方便地將源CSV檔案載入到DataFrame物件中。然後,您可以使用以下命令檢視已載入的DataFrame的標題和前幾行.head():
>>> climate_temp.head() STATION STATION_NAME ... DLY-HTDD-BASE60 DLY-HTDD-NORMAL 0 GHCND:USC00049099 TWENTYNINE PALMS CA US ... 10 15 1 GHCND:USC00049099 TWENTYNINE PALMS CA US ... 10 15 2 GHCND:USC00049099 TWENTYNINE PALMS CA US ... 10 15 3 GHCND:USC00049099 TWENTYNINE PALMS CA US ... 10 15 4 GHCND:USC00049099 TWENTYNINE PALMS CA US ... 10 15 >>> climate_precip.head() STATION ... DLY-SNOW-PCTALL-GE050TI 0 GHCND:USC00049099 ... -9999 1 GHCND:USC00049099 ... -9999 2 GHCND:USC00049099 ... -9999 3 GHCND:USC00049099 ... 0 4 GHCND:USC00049099 ... 0
在這裡,您曾經.head()獲得每個DataFrame的前五行。確保使用互動式Jupyter Notebook或在控制檯中自行嘗試,以便您可以更深入地瀏覽資料。 接下來,快速瀏覽兩個DataFrame的尺寸: >>> climate_temp.shape (127020, 21) >>> climate_precip.shape (151110, 29) 請注意,這.shape是DataFrame物件的屬性,它告訴您DataFrame的尺寸。對於climate_temp,輸出的結果.shape表明DataFrame具有127,020行和21列。 內聯 在此示例中,將使用merge()其預設引數,這將導致內部聯接。請記住,在內部聯接中,您將丟失其他DataFrame的key列中不匹配的行。 將這兩個資料集載入到DataFrame物件中後,您將選擇降水資料集的一小部分,然後使用普通merge()呼叫進行內部聯接。這將產生一個更小,更集中的資料集: >>> precip_one_station = climate_precip[climate_precip["STATION"] == "GHCND:USC00045721"] >>> precip_one_station.head() STATION ... DLY-SNOW-PCTALL-GE050TI 1460 GHCND:USC00045721 ... -9999 1461 GHCND:USC00045721 ... -9999 1462 GHCND:USC00045721 ... -9999 1463 GHCND:USC00045721 ... -9999 1464 GHCND:USC00045721 ... -9999 在這裡,您建立了一個precip_one_station從DataFrame呼叫的新DataFrame climate_precip,僅選擇了STATION欄位為的行"GHCND:USC00045721"。 如果檢查該shape屬性,則將看到它有365行。合併時,您認為合併的DataFrame中會得到多少行?請記住,您將進行內部聯接:
precip_one_station = climate_precip[climate_precip["STATION"] == "GHCND:USC00045721"] precip_one_station.head()
>>> inner_merged = pd.merge(precip_one_station, climate_temp) >>> inner_merged.head() STATION STATION_NAME ... DLY-HTDD-BASE60 DLY-HTDD-NORMAL 0 GHCND:USC00045721 MITCHELL CAVERNS CA US ... 14 19 1 GHCND:USC00045721 MITCHELL CAVERNS CA US ... 14 19 2 GHCND:USC00045721 MITCHELL CAVERNS CA US ... 14 19 3 GHCND:USC00045721 MITCHELL CAVERNS CA US ... 14 19 4 GHCND:USC00045721 MITCHELL CAVERNS CA US ... 14 19 >>> inner_merged.shape (365, 47) 如果您猜到了365行,那麼您是正確的!這是因為merge()預設為內部聯接,而內部聯接將僅丟棄不匹配的那些行。由於您所有的行都有匹配項,因此沒有丟失。您還應該注意,現在還有更多列:確切地說是47列。 使用merge(),您還可以控制要加入的列。假設您要合併兩個資料集,但只能合併Station,Date因為兩者的結合將為每一行產生唯一的值。為此,可以使用on引數:
inner_merged = pd.merge(precip_one_station, climate_temp)
inner_merged.head()
inner_merged.shape
您可以使用字串指定單個鍵列,也可以使用列表指定多個鍵列。這將導致DataFrame具有123,005行和48列。
為什麼用48列而不是47列?因為您指定了要連線的鍵列,所以Pandas不會嘗試合併所有可合併列。這可能會導致“重複”的列名,它們可能具有也可能沒有不同的值。
用引號引起來“重複”,因為列名將不完全匹配。預設情況下,它們附加_x和_y。您還可以使用suffixes引數來控制附加到列名稱的內容。
為防止意外,以下所有示例將使用on引數指定要連線的一個或多個列。
外連線
在這裡,您將使用how引數指定外部聯接。請記住,從上圖中可以看出,在外部聯接(也稱為完全外部聯接)中,兩個DataFrame中的所有行都將出現在新DataFrame中。
如果一行在另一個DataFrame中沒有匹配項(基於鍵列),則不會像使用內部聯接那樣丟失該行。相反,該行將在合併的DataFrame中,NaN並在適當的地方填充值。
最好在一個示例中說明:
outer_merged = pd.merge(precip_one_station, climate_temp, how="outer", on=["STATION", "DATE"]) outer_merged.head() outer_merged.shape
如果您從檢查的.shape屬性時還記得climate_temp,那麼您會發現其中的行數outer_merged是相同的。通過外部聯接,可以期望與較大的DataFrame具有相同的行數。這是因為,即使在另一個DataFrame中沒有匹配項,在外部聯接中也不會丟失任何行。 左加入 在這個例子中,你會指定一個LEFT JOIN,也稱為左外連線 -附的how引數。使用左外部聯接將使新合併的DataFrame保留左側資料框架中的所有行,同時丟棄右側資料框架中在左側DataFrame的關鍵列中不匹配的行。 您可以將其視為半外半內的合併。下面的示例向您展示了這一功能:
left_merged = pd.merge(climate_temp, precip_one_station,how="left", on=["STATION", "DATE"]) left_merged.head() left_merged.shape
left_merged有127,020行,與左側DataFrame中的行數匹配climate_temp。為了證明這僅適用於左側的DataFrame,請執行相同的程式碼,但更改precip_one_stationand 的位置climate_temp:
left_merged_reversed = pd.merge(precip_one_station, climate_temp, how="left", on=["STATION", "DATE"]) left_merged_reversed.head() left_merged_reversed.shape
這將導致DataFrame具有365行,與中的行數匹配precip_one_station。 右加入 右連線(或右外部連線)是左連線的映象版本。通過此聯接,將保留右側DataFrame中的所有行,而左側DataFrame中與右側DataFrame的鍵列中不匹配的行將被丟棄。 為了演示左右聯接是如何相互映象的,在下面的示例中,您將left_merged使用右聯接從上方重新建立DataFrame: right_merged = pd.merge(precip_one_station, climate_temp, how="right", on=["STATION", "DATE"]) right_merged.head() right_merged.shape 在這裡,您只需翻轉輸入DataFrame的位置並指定右連線。檢查時right_merged,您可能會注意到它與並不完全相同left_merged。兩者之間的唯一區別是列的順序:第一個輸入的列將始終是新形成的DataFrame中的第一個列。 merge()是Pandas資料組合工具中最複雜的。這也是構建其他工具的基礎。它的複雜性是其最大的優勢,它使您能夠以各種方式組合資料集並生成對資料的新見解。 另一方面,merge()如果沒有對集合論和資料庫操作的直觀瞭解,那麼這種複雜性將使其難以使用。在本節中,您學習了各種資料合併技術以及多對一和多對多合併,這些合併最終來自集合論。有關集合理論的更多資訊,請檢視Python中的集合。 現在,您將看到的簡化版本merge():.join()。 熊貓.join():合併列或索引上的資料 雖然merge()是模組函式,但是.join()是駐留在DataFrame上的物件函式。這使您僅可以指定一個DataFrame,它將加入您所呼叫的DataFrame .join()。 在幕後,.join()使用merge(),但與完全指定的merge()呼叫相比,它提供了一種更有效的方法來連線DataFrame 。在深入探討可用選項之前,請看以下簡短示例: precip_one_station.join(climate_temp, lsuffix="_left", rsuffix="_right") 在索引可見的情況下,您可以看到這裡發生了左連線,precip_one_station即為左DataFrame。您可能會注意到,此示例提供了引數lsuffix和rsuffix。因為.join()在索引上聯接並且不直接合並DataFrame,所以所有列(即使是具有匹配名稱的列)都保留在結果DataFrame中。 如果翻轉前面的示例,而是呼叫.join()較大的DataFrame,則會注意到該DataFrame較大,但是較小的DataFrame(precip_one_station)中不存在的資料將填充以下NaN值: climate_temp.join(precip_one_station, lsuffix="_left", rsuffix="_right") 如何.join() 預設情況下,.join()將嘗試對索引進行左連線。如果要像使用一樣加入列merge(),則需要將這些列設定為索引。 像一樣merge(),.join()具有一些引數,可以為您的聯接提供更大的靈活性。但是,使用時.join(),引數列表相對較短: other:這是唯一必需的引數。它定義了另一個要聯接的DataFrame。您還可以在此處指定DataFrames列表,從而允許您在一次.join()呼叫中合併多個數據集。 on:此引數為左側DataFrame(climate_temp在上一個示例中)指定一個可選的列或索引名稱,以連線otherDataFrame的索引。如果將其設定為None,這是預設設定,則聯接將為index-on-index。 how:此選項與howfrom 相同merge()。區別在於,它是基於索引的,除非您還使用指定了列on。 lsuffix和rsuffix:這是類似於suffixes在merge()。它們指定字尾以新增到任何重疊的列,但在傳遞otherDataFrame 列表時無效。 sort:啟用此功能可以通過連線鍵對結果DataFrame進行排序。 範例 在本部分中,您將看到一些示例,這些示例顯示的一些不同用例.join()。有些會簡化merge()通話。其他功能將.join()與更冗長的merge()呼叫區分開。 由於您已經看到了一個簡短的.join()通話,因此在第一個示例中,您將嘗試使用建立一個merge()通話.join()。這需要什麼?花點時間考慮一下可能的解決方案,然後在下面檢視建議的解決方案: inner_merged_total = pd.merge(climate_temp, climate_precip, on=["STATION", "DATE"]) inner_merged_total.head() inner_joined_total = climate_temp.join( climate_precip.set_index(["STATION", "DATE"]), lsuffix="_x", rsuffix="_y", on=["STATION", "DATE"], ) inner_joined_total.head() 因為.join()對索引有效,所以如果我們想merge()從之前重新建立,則必須在指定的聯接列上設定索引。在此示例中,您曾經.set_index()將索引設定為聯接內的關鍵列。 有了這個,之間的連線merge(),並.join()應該是比較清楚。 在下面,您將看到一個幾乎是裸露的.join()電話。因為有重疊的列,你就需要使用指定字尾lsuffix,rsuffix或兩者,但這個例子將展示的更典型行為.join(): climate_temp.join(climate_precip, lsuffix="_left") 這個例子應該讓人想起您在.join()前面的介紹中看到的內容。呼叫是相同的,導致左連線產生與相同行數的DataFrame cliamte_temp。 在本節中,您已經瞭解了.join()它的引數和用法。您還了解了幕後.join()工作原理,並重新建立了一個merge()呼叫.join()以更好地瞭解這兩種技術之間的聯絡。 熊貓concat():結合資料通過行或列 串聯與您在上面看到的合併技術有些不同。通過合併,您通常可以基於某些通用性,期望結果資料集將來自父資料集的行混合在一起。根據合併的型別,您可能還會丟失其他資料集中沒有匹配項的行。 使用串聯時,您的資料集僅沿著一個軸(行軸或列軸)縫合在一起。在視覺上,沿行不帶引數的串聯看起來像這樣: 沿軸0的串聯(行) 要在程式碼中實現此功能,您將使用要concat()傳遞的DataFrame列表並將其傳遞給它。此任務的程式碼如下所示: concatenated = pandas.concat([df1, df2]) 注意:此示例假定您的列名相同。如果在沿行(軸0)連線時,列名不同,則預設情況下還將新增列,並NaN在適用時填寫值。 相反,如果您想沿列進行串聯怎麼辦?首先,看一下該操作的直觀表示: 沿軸1的串聯(列) 為此,您將concat()像上面一樣使用一個呼叫,但是您還需要傳遞axis一個值為的引數1: concatenated = pandas.concat([df1, df2], axis=1) 注意:此示例假定資料集之間的索引相同。如果沿列(軸1)連線時它們不同,則預設情況下還將新增額外的索引(行),並NaN在適用時填充值。 您將concat()在以下部分中瞭解有關引數的更多資訊。如您所見,串聯是合併資料集的更簡單方法。它通常用於形成單個較大的集合,以對其執行其他操作。 注意:呼叫時concat(),將複製要連線的所有資料。您應該小心多次concat()通話,因為製作的許多副本可能會對效能產生負面影響。或者,您可以將可選copy引數設定為False 串聯資料集時,可以指定串聯的軸。但是另一根軸會發生什麼呢? 沒有。預設情況下,串聯會產生一個set union,其中將保留所有資料。通過merge()和.join()作為外部聯接,您已經看到了這一點,並且可以使用join引數指定它。 如果使用此引數,則選項outer(預設情況下)為和inner,這將執行內部聯接(或設定交集)。 與您先前看到的其他內部聯接一樣,使用進行內部聯接時可能會發生一些資料丟失concat()。僅在軸標籤匹配的地方,您才能保留行或列。 注意:請記住,該join引數僅指定如何處理未串聯的軸。 既然您已經瞭解了該join引數,那麼以下是一些其他concat()需要使用的引數: objs:此引數採用的任何序列(通常為列表)Series或DataFrame反對被級聯。您還可以提供字典。在這種情況下,鍵將用於構造層次結構索引。 axis:與其他技術一樣,它表示您將串聯的軸。預設值為0,它沿索引(或行軸)1連線,而沿列(垂直)連線。您也可以使用字串值index或columns。 join:這類似於how其他技術中的引數,但僅接受值inner或outer。預設值為outer,它將保留資料,而inner將消除其他資料集中不匹配的資料。 ignore_index:此引數為布林值(True或False),預設為False。如果為True,則新的組合資料集將不會在axis引數中指定的軸上保留原始索引值。這使您擁有全新的索引值。 keys:使用此引數可以構造層次結構索引。一種常見的用例是在保留原始索引的同時擁有一個新索引,這樣您就可以知道哪些行來自哪個原始資料集。 copy:此引數指定您是否要複製源資料。預設值為True。如果將該值設定為False,則Pandas將不會複製源資料。 此列表並不詳盡。您可以在Pandas文件中找到完整的最新引數列表。 如何使用新增到DataFrameappend() 在進入concat()示例之前,您應該瞭解.append()。這是concat()提供連線的更簡單,更嚴格的介面的快捷方式。您可以.append()同時在Series和DataFrame物件上使用,並且兩者的工作方式相同。 要使用.append(),請在可用的一個數據集上呼叫,然後將另一個數據集(或資料集列表)作為方法的引數傳遞: concatenated = df1.append(df2) pandas.concat([df1, df2])除了使用例項方法.append()而不是模組方法外,您在此處所做的操作與呼叫時相同concat()。 範例 首先,您將使用在本教程中一直使用的DataFrame沿預設軸進行基本串聯: double_precip = pd.concat([precip_one_station, precip_one_station]) 通過設計,這一步驟非常簡單。在這裡,您建立了一個DataFrame,它是先前製作的小型DataFrame的兩倍。要注意的一件事是索引重複。如果您想要一個新的,從0開始的索引,則可以使用ignore_index引數: reindexed = pd.concat([precip_one_station, precip_one_station], ignore_index=True) 如前所述,如果沿軸0(行)連線,但軸1(列)中的標籤不匹配,則將新增這些標籤並用NaN值填充。這導致外部聯接: outer_joined = pd.concat([climate_precip, climate_temp]) 使用這兩個DataFrame,由於您只是沿行串聯,因此很少有列具有相同的名稱。這意味著您將看到很多帶有NaN值的列。 要改為刪除缺少任何資料的列,請使用join帶有值的引數"inner"進行內部聯接: inner_joined = pd.concat([climate_temp, climate_precip], join="inner") 使用內部聯接,你會留下只有那些列,原來DataFrames的共同點:STATION,STATION_NAME,和DATE。 您還可以通過設定axis引數來翻轉它: inner_joined_cols = pd.concat([climate_temp, climate_precip], axis=1, join="inner") 現在,在兩個DataFrame中,只有行包含所有列的資料。行數與較小的DataFrame的行數相對應並非巧合。 串聯的另一個有用技巧是使用keys引數建立分層軸標籤。如果您想保留原始資料集的索引或列名,但又想將新的索引或列名向上一級儲存,這將非常有用: hierarchical_keys = pd.concat([climate_temp, climate_precip], keys=["temp", "precip"]) 如果檢查原始的DataFrame,則可以驗證是否將更高級別的軸標籤temp和precip新增到了適當的行。 最後,看看第一個改寫為使用的串聯示例.append(): appended = precip_one_station.append(precip_one_station) 注意,using的結果與本節開始.append()時使用的結果相同concat()。 您現在已經瞭解了在熊貓中組合資料的三種最重要的技術: merge() 用於合併公共列或索引上的資料 .join() 用於組合鍵列或索引上的資料 concat() 用於跨行或跨列組合DataFrame 除了學習如何使用這些技術之外,您還通過試驗加入資料集的不同方法來了解集合邏輯。您還了解了上述技術的API和類似的替代呼叫.append(),可以用來簡化程式碼。
原文連結:https://realpython.com/pandas-merge-join-and-concat/