微信公眾號:「Python讀財」
如有問題或建議,請公眾號留言
在日常的資料處理中,經常會對一個DataFrame
進行逐行、逐列和逐元素的操作,對應這些操作,Pandas中的map
、apply
和applymap
可以解決絕大部分這樣的資料處理需求。這篇文章就以案例附帶圖解的方式,為大家詳細介紹一下這三個方法的實現原理,相信讀完本文後,不論是小白還是Pandas的進階學習者,都會對這三個方法有更深入的理解。
本文演示的資料集是模擬生成的,想練手的可以按下方的程式碼生成。
boolean=[True,False]
gender=["男","女"]
color=["white","black","yellow"]
data=pd.DataFrame({
"height":np.random.randint(150,190,100),
"weight":np.random.randint(40,90,100),
"smoker":[boolean[x] for x in np.random.randint(0,2,100)],
"gender":[gender[x] for x in np.random.randint(0,2,100)],
"age":np.random.randint(15,90,100),
"color":[color[x] for x in np.random.randint(0,len(color),100) ]
}
)
資料集如下所示,各列分別代表身高、體重、是否吸菸、性別、年齡和膚色。
map用法
如果需要把資料集中gender
列的男替換為1,女替換為0,怎麼做呢?絕對不是用for迴圈實現,使用Series.map()
可以很容易做到,最少僅需一行程式碼。
#①使用字典進行對映
data["gender"] = data["gender"].map({"男":1, "女":0})
#②使用函式
def gender_map(x):
gender = 1 if x == "男" else 0
return gender
#注意這裡傳入的是函式名,不帶括號
data["gender"] = data["gender"].map(gender_map)
那map
在實際過程中是怎麼執行的呢?請看下面的圖解(為了方便展示,僅擷取了前10條資料)
不論是利用字典還是函式進行對映,map
方法都是把對應的資料逐個當作引數傳入到字典或函式中,得到對映後的值。
apply
同時Series物件還有apply
方法,apply
方法的作用原理和map
方法類似,區別在於apply
能夠傳入功能更為複雜的函式。怎麼理解呢?一起看看下面的例子。
假設在資料統計的過程中,年齡age
列有較大誤差,需要對其進行調整(加上或減去一個值),由於這個加上或減去的值未知,故在定義函式時,需要加多一個引數bias
,此時用map
方法是操作不了的(傳入map
的函式只能接收一個引數),apply
方法則可以解決這個問題。
def apply_age(x,bias):
return x+bias
#以元組的方式傳入額外的引數
data["age"] = data["age"].apply(apply_age,args=(-3,))
可以看到age列都減了3,當然,這裡只是簡單舉了個例子,當需要進行復雜處理時,更能體現apply
的作用。
總而言之,對於Series而言,map
可以解決絕大多數的資料處理需求,但如果需要使用較為複雜的函式,則需要用到apply
方法。
apply
對DataFrame
而言,apply
是非常重要的資料處理方法,它可以接收各種各樣的函式(Python內建的或自定義的),處理方式很靈活,下面透過幾個例子來看看apply
的具體使用及其原理。
在進行具體介紹之前,首先需要介紹一下DataFrame
中axis
的概念,在DataFrame
物件的大多數方法中,都會有axis
這個引數,它控制了你指定的操作是沿著0軸還是1軸進行。axis=0
代表操作對列columns
進行,axis=1
代表操作對行row
進行,如下圖所示。
如果還不是很瞭解,沒關係,下面會分別對apply
沿著0軸以及1軸的操作進行講解,繼續往下走。
假設現在需要對data
中的數值列分別進行取對數和求和的操作,這時可以用apply
進行相應的操作,因為是對列進行操作,所以需要指定axis=0
,使用下面的兩行程式碼可以很輕鬆地解決我們的問題。
# 沿著0軸求和
data[["height","weight","age"]].apply(np.sum, axis=0)
# 沿著0軸取對數
data[["height","weight","age"]].apply(np.log, axis=0)
實現的方式很簡單,但呼叫apply
時究竟發生了什麼呢?過程是怎麼實現的?還是透過圖解的方式來一探究竟。(取前五條資料為例)
當沿著軸0(axis=0)
進行操作時,會將各列(columns
)預設以Series
的形式作為引數,傳入到你指定的操作函式中,操作後合併並返回相應的結果。
那如果在實際使用中需要按行進行操作(axis=1
),那整個過程又是怎麼實現的呢?
在資料集中,有身高和體重的資料,所以根據這個,我們可以計算每個人的BMI指數(體檢時常用的指標,衡量人體肥胖程度和是否健康的重要標準),計算公式是:體重指數BMI=體重/身高的平方(國際單位kg/㎡)
,因為需要對每個樣本進行操作,這裡使用axis=1
的apply
進行操作,程式碼如下:
def BMI(series):
weight = series["weight"]
height = series["height"]/100
BMI = weight/height**2
return BMI
data["BMI"] = data.apply(BMI,axis=1)
還是用圖解的方式來看看這個過程到底是怎麼實現的(以前5條資料為例)。
當apply
設定了axis=1
對行進行操作時,會預設將每一行資料以Series
的形式(Series的索引為列名)傳入指定函式,返回相應的結果。
總結一下對DataFrame
的apply
操作:
- 當
axis=0
時,對每列columns
執行指定函式;當axis=1
時,對每行row
執行指定函式。 - 無論
axis=0
還是axis=1
,其傳入指定函式的預設形式均為Series
,可以透過設定raw=True
傳入numpy陣列
。 - 對每個Series執行結果後,會將結果整合在一起返回(若想有返回值,定義函式時需要
return
相應的值) - 當然,
DataFrame
的apply
和Series
的apply
一樣,也能接收更復雜的函式,如傳入引數等,實現原理是一樣的,具體用法詳見官方文件。
applymap
applymap
的用法比較簡單,會對DataFrame
中的每個單元格執行指定函式的操作,雖然用途不如apply
廣泛,但在某些場合下還是比較有用的,如下面這個例子。
為了演示的方便,新生成一個DataFrame
df = pd.DataFrame(
{
"A":np.random.randn(5),
"B":np.random.randn(5),
"C":np.random.randn(5),
"D":np.random.randn(5),
"E":np.random.randn(5),
}
)
df
現在想將DataFrame
中所有的值保留兩位小數顯示,使用applymap
可以很快達到你想要的目的,程式碼和圖解如下:
df.applymap(lambda x:"%.2f" % x)
資料處理三板斧就介紹到這裡,有問題歡迎下方積極留言呀!
掃碼關注公眾號「Python讀財」,第一時間獲取乾貨,還可以加Python學習交流群!!
本作品採用《CC 協議》,轉載必須註明作者和本文連結