詳解Pandas如何高效對(duì)比處理DataFrame的兩列數(shù)據(jù)
楔子
我們?cè)谟?pandas 處理數(shù)據(jù)的時(shí)候,經(jīng)常會(huì)遇到用其中一列數(shù)據(jù)替換另一列數(shù)據(jù)的場(chǎng)景。比如 A 列和 B 列,對(duì) A 列中不為空的數(shù)據(jù)不作處理,對(duì) A 列中為空的數(shù)據(jù)使用 B 列對(duì)應(yīng)索引的數(shù)據(jù)進(jìn)行替換。這一類的需求估計(jì)很多人都遇到,當(dāng)然還有其它更復(fù)雜的。
解決這類需求的辦法有很多,這里我們來(lái)推薦幾個(gè)。
combine_first
這個(gè)方法是專門用來(lái)針對(duì)空值處理的,我們來(lái)看一下用法。
import?pandas?as?pd df?=?pd.DataFrame( ????{"A":?["001",?None,?"003",?None,?"005"], ?????"B":?["1",?"2",?"3",?"4",?"5"]} ) print(df) """ ??????A??B 0???001??1 1??None??2 2???003??3 3??None??4 4???005??5 """ #?我們現(xiàn)在需求如下,如果?A?列中的數(shù)據(jù)不為空,那么不做處理 #?如果為空,則用?B?列中對(duì)應(yīng)的數(shù)據(jù)進(jìn)行替換 df["A"]?=?df["A"].combine_first(df["B"]) print(df) """ ?????A??B 0??001??1 1????2??2 2??003??3 3????4??4 4??005??5 """
使用方法很簡(jiǎn)單,首先是兩個(gè) Series 對(duì)象,假設(shè)叫 s1 和 s2,那么 s1.combine_first(s2) 就表示用 s2 替換掉 s1 中為空的數(shù)據(jù)。如果 s1 和 s2 的某個(gè)相同索引對(duì)應(yīng)的數(shù)據(jù)都是空,那么結(jié)果只能是空。當(dāng)然這個(gè)方法不是在原地操作,而是會(huì)返回一個(gè)新的 Series 對(duì)象。
另外這個(gè)方法的理想前提是兩個(gè) Series 對(duì)象的索引是一致的,因?yàn)樘鎿Q是根據(jù)索引來(lái)指定位置的,舉個(gè)例子。
import?pandas?as?pd s1?=?pd.Series(["001",?None,?None,?"004"],? ???????????????index=['a',?'b',?'c',?'d']) s2?=?pd.Series(["2",?"3",?"4"],? ???????????????index=['b',?'d',?"e"]) print(s1) """ a?????001 b????None c????None d?????004 dtype:?object """ print(s2) """ b????2 d????3 e????4 dtype:?object """ print(s1.combine_first(s2)) """ a????001 b??????2 c????NaN d????004 e??????4 dtype:?object """
解釋一下,首先替換的都是 s1 中值為空的數(shù)據(jù),如果不為空那么不做任何處理。s1 中值為空的數(shù)據(jù)有兩個(gè),索引分別為 b、c,那么會(huì)用 s2 中索引為 b、c 的數(shù)據(jù)進(jìn)行替換。但 s2 中只存在索引為 b、不存在索引為 c 的數(shù)據(jù),那么就只能替換一個(gè)值。
另外我們看到結(jié)尾還多了個(gè)索引為 e 的數(shù)據(jù),是的,如果 s2 中的數(shù)據(jù),s1 沒(méi)有,那么會(huì)直接加上去。
注意:pandas 的很多操作都是基于自帶的索引進(jìn)行的,并不是簡(jiǎn)單的從上往下一一對(duì)應(yīng)。即便是很多 pandas 老手,偶爾也會(huì)犯這個(gè)錯(cuò)誤。
當(dāng)然大部分情況下我們處理的都是同一個(gè) DataFrame 的兩列,對(duì)于同一個(gè) DataFrame 中的兩列,它們的索引顯然是一致的,所以就是簡(jiǎn)單的從上到下,不會(huì)有太多花里胡哨的。
combine
combine 和 combine_first 類似,只是需要指定一個(gè)函數(shù)。
import?pandas?as?pd df?=?pd.DataFrame( ????{"A":?["001",?None,?"003",?None,?"005"], ?????"B":?["1",?"2",?"3",?"4",?"5"]} ) print(df) """ ??????A??B 0???001??1 1??None??2 2???003??3 3??None??4 4???005??5 """ df["A"]?=?df["A"].combine(df["B"],? ??????????????????????????lambda?a,?b:?a?if?pd.notna(a)?else?b) print(df) """ ?????A??B 0??001??1 1????2??2 2??003??3 3????4??4 4??005??5 """
我們指定了一個(gè)匿名函數(shù),參數(shù) a、b 就代表 df["A"] 和 df["B"] 中對(duì)應(yīng)的每一個(gè)數(shù)據(jù)。如果 a 不為空,那么返回 a,否則返回 b。
所以我們使用 combine 實(shí)現(xiàn)了 combine_first 的功能,combine_first 是專門對(duì)空值進(jìn)行替換的,但 combine 則是可以讓我們自己指定邏輯。我們可以實(shí)現(xiàn) combine_first 的功能,也可以實(shí)現(xiàn)其它的功能。
import?pandas?as?pd s1?=?pd.Series([1,?22,?3,?44]) s2?=?pd.Series([11,?2,?33,?4]) #?哪個(gè)元素大就保留哪一個(gè) print(s1.combine(s2,?lambda?a,?b:?a?if?a?>?b?else?b)) """ 0????11 1????22 2????33 3????44 dtype:?int64 """ #?兩個(gè)元素進(jìn)行相乘 #?當(dāng)然,對(duì)于目前這個(gè)需求,最好的辦法是?s1?*?s2 print(s1.combine(s2,?lambda?a,?b:?a?*?b)) """ 0?????11 1?????44 2?????99 3????176 dtype:?int64 """
combine 用起來(lái)還是很方便的,當(dāng)然它同樣是針對(duì)索引來(lái)操作的。此外combine和combine_first內(nèi)部都會(huì)先對(duì)索引進(jìn)行處理,如果兩個(gè) Series 對(duì)象的索引不一樣,那么會(huì)先讓它們索引變得一致。
import?pandas?as?pd s1?=?pd.Series([1,?22,?3,?44],?index=['a',?'b',?'c',?'d']) s2?=?pd.Series([11,?2,?33,?4],?index=['c',?'d',?'e',?'f']) #?先對(duì)兩個(gè)索引取并集 index?=?s1.index.union(s2.index) print(index)? """ Index(['a',?'b',?'c',?'d',?'e',?'f'],?dtype='object') """ #?然后通過(guò)reindex,獲取指定索引的元素 #?索引不存在就用?NaN?代替 s1?=?s1.reindex(index) s2?=?s2.reindex(index) print(s1) """ a?????1.0 b????22.0 c?????3.0 d????44.0 e?????NaN f?????NaN dtype:?float64 """ print(s2) """ a?????NaN b?????NaN c????11.0 d?????2.0 e????33.0 f?????4.0 dtype:?float64 """
combine 和 combine_first 都是先讓 s1 和 s2 的索引變得一致之后,再進(jìn)行操作。
import?pandas?as?pd s1?=?pd.Series([1,?22,?3,?44], ???????????????index=['a',?'b',?'c',?'d']) s2?=?pd.Series([11,?2,?33,?4], ???????????????index=['c',?'d',?'e',?'f']) print(s1.combine_first(s2)) """ a?????1.0 b????22.0 c?????3.0 d????44.0 e????33.0 f?????4.0 dtype:?float64 """
所以你會(huì)發(fā)現(xiàn),s1 和 s2 里面都沒(méi)有空值,返回的結(jié)果也沒(méi)有空值,但是類型卻從整型變成了浮點(diǎn)型。就是因?yàn)?s1 和 s2 在 reindex 的過(guò)程中出現(xiàn)了 NaN,所以類型變成了浮點(diǎn)型。
所以在使用 combine 和 combine_first 這兩個(gè)方法的時(shí)候,一定要記住索引,否則可能會(huì)造成陷阱。事實(shí)上,包括 pandas 很多的其它操作也是,它們都是基于索引來(lái)的,并不是簡(jiǎn)單的依次從左到右或者從上到下。
update
update 比較野蠻,我們來(lái)看一下。
import?pandas?as?pd s1?=?pd.Series([1,?2,?3,?4]) s2?=?pd.Series([11,?22,?33,?44]) s1.update(s2) print(s1) """ 0????11 1????22 2????33 3????44 dtype:?int64 """
首先我們看到這個(gè)方法是在本地進(jìn)行操作的,功能還是用 s2 的元素替換 s1 的元素,并且只要 s2 中的元素不為空,那么就進(jìn)行替換。
import?pandas?as?pd s1?=?pd.Series([1,?2,?3,?4]) s2?=?pd.Series([11,?22,?None,?44]) s1.update(s2) print(s1) """ 0????11 1????22 2?????3 3????44 dtype:?int64 """
所以這個(gè)函數(shù)叫 update,意思就是更新。用 s2 中的元素?fù)Q掉 s1 中的元素。但如果 s2 中的元素為空,那么可以認(rèn)為新版本還沒(méi)出來(lái),那么還是使用老版本,所以 s1 中的 3 沒(méi)有被換掉。
因此 update 和 combine_first 比較類似,但它們的區(qū)別在于:
- combine_first:如果 s1 中的值為空,用 s2 的值替換,否則保留 s1 的值;
- update:如果 s2 中的值不為空,那么替換 s1,否則保留 s1 的值;
另外在 combine_first 的時(shí)候,我們反復(fù)強(qiáng)調(diào)了索引的問(wèn)題,如果 s1 和 s2 索引不一樣,那么生成的結(jié)果的元素個(gè)數(shù)會(huì)增多。但是 update 不同,因?yàn)樗窃诒镜剡M(jìn)行操作的,也就是直接本地修改 s1,所以最終 s1 的元素個(gè)數(shù)是不會(huì)發(fā)生變化的。
import?pandas?as?pd s1?=?pd.Series([1,?2,?3,?4],? ???????????????index=['a',?'b',?'c',?'d']) s2?=?pd.Series([11,?22,?33,?44],? ???????????????index=['c',?'d',?'e',?'f']) s1.update(s2) print(s1) """ a?????1 b?????2 c????11 d????22 dtype:?int64 """
s2 中不存在 index 為 a、b 的元素,那么可以認(rèn)為新版本沒(méi)有出現(xiàn),因此不更新、保留原來(lái)的值。但 s2 中存在 index 為 c、d 的元素,所以有新版本,那么就更新。所以 s1 由 [1 2 3 4] 變成了 [1 2 11 22]。
至于 s2 中 index 為 e、f 的元素,它們和 s1 沒(méi)有關(guān)系,因?yàn)?s1 中壓根沒(méi)有 index 為 e、f 的元素,s2 提供了新版本也是沒(méi)用的。所以使用 update,是在 s1 本地操作的,操作前后 s1 的索引以及元素個(gè)數(shù)不會(huì)改變。
當(dāng)然 update 也適用于對(duì)兩個(gè) DataFrame 進(jìn)行操作,有興趣可以自己去了解,但大部分時(shí)候我們都用在 Series 上面。
到此這篇關(guān)于詳解Pandas如何高效對(duì)比處理DataFrame的兩列數(shù)據(jù)的文章就介紹到這了,更多相關(guān)Pandas處理DataFrame數(shù)據(jù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python中docx2txt庫(kù)的使用說(shuō)明
這篇文章主要介紹了Python中docx2txt庫(kù)的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03Python利用Flask-Mail實(shí)現(xiàn)發(fā)送郵件詳解
Flask?的擴(kuò)展包?Flask?-?Mail?通過(guò)包裝了?Python?內(nèi)置的smtplib包,可以用在?Flask?程序中發(fā)送郵件。本文將利用這特性實(shí)現(xiàn)郵件發(fā)送功能,感興趣的可以了解一下2022-08-08Python數(shù)據(jù)可視化圖實(shí)現(xiàn)過(guò)程詳解
這篇文章主要介紹了Python數(shù)據(jù)可視化圖實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06Python調(diào)用SQLPlus來(lái)操作和解析Oracle數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了Python調(diào)用SQLPlus來(lái)操作和解析Oracle數(shù)據(jù)庫(kù)的方法,這樣用SQL*Plus方式來(lái)分析Oracle中的數(shù)據(jù)就變得十分方便,需要的朋友可以參考下2016-04-04解決Pycharm在Debug的時(shí)候一直“Connected”沒(méi)有下一步動(dòng)作問(wèn)題
這篇文章主要介紹了解決Pycharm在Debug的時(shí)候一直“Connected”沒(méi)有下一步動(dòng)作問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08Python面向?qū)ο笾鄳B(tài)原理與用法案例分析
這篇文章主要介紹了Python面向?qū)ο笾鄳B(tài)原理與用法,結(jié)合具體案例形式分析了Python多態(tài)的具體功能、原理、使用方法與操作注意事項(xiàng),需要的朋友可以參考下2019-12-12使用Python實(shí)現(xiàn)合并多個(gè)Excel文件
合并Excel可以將多個(gè)文件中的數(shù)據(jù)合并到一個(gè)文件中,這樣可以幫助我們更好地匯總和管理數(shù)據(jù),本文主要介紹了如何使用第三方Python庫(kù) Spire.XLS for Python 實(shí)現(xiàn)以上兩種合并Excel文件的需求,有需要的可以了解下2023-12-12