在日常的數據處理中,經常會對一個DataFrame進行逐行、逐列和逐元素的操作,對應這些操作,Pandas中的map、apply和applymap可以解決絕大部分這樣的數據處理需求。
import pandas as pd
import numpy as np
# 創建數據集
boolean = ['True','False']
gender = ['男','女']
color = ['white','black','green']
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,len(boolean),100)],
'gender':[gender[x] for x in np.random.randint(0,len(gender),100)],
'age':np.random.randint(15,90,100),
'color':[color[x] for x in np.random.randint(0,len(color),100)]
})
data.head()
height | weight | smoker | gender | age | color | |
---|---|---|---|---|---|---|
0 | 189 | 70 | False | 女 | 72 | green |
1 | 153 | 42 | False | 女 | 49 | black |
2 | 178 | 51 | False | 女 | 50 | white |
3 | 170 | 47 | True | 男 | 85 | black |
4 | 163 | 51 | False | 男 | 89 | green |
Series數據處理
1. map
如果需要把數據集中的gender列的男替換爲1,女替換爲0。應該怎麼做呢?
# 使用字典映射
data['gender_x'] = data['gender'].map({'男':1,'女':0})
data.head()
height | weight | smoker | gender | age | color | gender_x | |
---|---|---|---|---|---|---|---|
0 | 189 | 70 | False | 女 | 72 | green | 0 |
1 | 153 | 42 | False | 女 | 49 | black | 0 |
2 | 178 | 51 | False | 女 | 50 | white | 0 |
3 | 170 | 47 | True | 男 | 85 | black | 1 |
4 | 163 | 51 | False | 男 | 89 | green | 1 |
# 使用函數
def gender_map(x):
gender = 1 if x =='男' else 0
return gender
# 傳入的函數名,不帶括號
data['gender_x'] = data['gender'].map(gender_map)
data.head()
height | weight | smoker | gender | age | color | gender_x | |
---|---|---|---|---|---|---|---|
0 | 189 | 70 | False | 女 | 72 | green | 0 |
1 | 153 | 42 | False | 女 | 49 | black | 0 |
2 | 178 | 51 | False | 女 | 50 | white | 0 |
3 | 170 | 47 | True | 男 | 85 | black | 1 |
4 | 163 | 51 | False | 男 | 89 | green | 1 |
不論是利用字典還是函數進行映射,map方法都是把對應的數據逐個當作參數傳入到字典或函數中,得到映射後的值。
2. apply
同時Series對象還有apply方法,apply方法的作用原理和map方法類似,區別在於apply能夠傳入功能更爲複雜的函數。怎麼理解呢?一起看看下面的例子。
假設在數據統計的過程中,年齡age列有較大誤差,需要對其進行調整(加上或減去一個值),由於這個加上或減去的值未知,故在定義函數時,需要加多一個參數bias,此時用map方法是操作不了的**(傳入map的函數只能接收一個參數)**,apply方法則可以解決這個問題。
def age_apply(x,bias):
return x+bias
# 額外的參數需要單獨給到
data['age_x'] = data['age'].apply(age_apply,bias=-3)
data.head()
height | weight | smoker | gender | age | color | gender_x | age_x | |
---|---|---|---|---|---|---|---|---|
0 | 189 | 70 | False | 女 | 72 | green | 0 | 69 |
1 | 153 | 42 | False | 女 | 49 | black | 0 | 46 |
2 | 178 | 51 | False | 女 | 50 | white | 0 | 47 |
3 | 170 | 47 | True | 男 | 85 | black | 1 | 82 |
4 | 163 | 51 | False | 男 | 89 | green | 1 | 86 |
對於Series而言,map可以解決絕大多數的數據處理需求,但如果需要使用較爲複雜的函數,則需要用到apply方法。
DataFrame數據
1. apply
對DataFrame而言,apply是非常重要的數據處理方法,它可以接收各種各樣的函數(Python內置的或自定義的),處理方式很靈活,下面通過幾個例子來看看apply的具體使用及其原理。
在進行具體介紹之前,首先需要介紹一下DataFrame中axis的概念,在DataFrame對象的大多數方法中,都會有axis這個參數,它控制了你指定的操作是沿着0軸還是1軸進行。axis=0代表操作對列columns進行,axis=1代表操作對行row進行。
假設現在需要對data中的數值列分別進行取對數和求和的操作,這時可以用apply進行相應的操作,因爲是對列進行操作,所以需要指定axis=0
# 沿着0軸求和
df1 = data[['height','weight','age']].apply(np.sum,axis=0)
df1
height 16964
weight 6268
age 4728
dtype: int64
df2 = data[['height','weight','age']].apply(np.log,axis=0)
df2
height | weight | age | |
---|---|---|---|
0 | 5.241747 | 4.248495 | 4.276666 |
1 | 5.030438 | 3.737670 | 3.891820 |
2 | 5.181784 | 3.931826 | 3.912023 |
3 | 5.135798 | 3.850148 | 4.442651 |
4 | 5.093750 | 3.931826 | 4.488636 |
... | ... | ... | ... |
95 | 5.056246 | 4.007333 | 4.430817 |
96 | 5.068904 | 4.343805 | 4.127134 |
97 | 5.170484 | 4.418841 | 4.356709 |
98 | 5.198497 | 4.304065 | 3.433987 |
99 | 5.220356 | 4.465908 | 3.178054 |
100 rows × 3 columns
當沿着軸0(axis=0)進行操作時,會將各列(columns)默認以Series的形式作爲參數,傳入到你指定的操作函數中,操作後合併並返回相應的結果。
在數據集中,有身高和體重的數據,所以根據這個,我們可以計算每個人的BMI指數(體檢時常用的指標,衡量人體肥胖程度和是否健康的重要標準),計算公式是:體重指數BMI=體重/身高的平方(國際單位kg/㎡),因爲需要對每個樣本進行操作,這裏使用axis=1的apply進行操作,代碼如下:
def BMI(series):
weight = series['weight']
height = series['height']
BMI = weight/height**2
return BMI
data['BMI'] = data.apply(BMI,axis=1)
data.head()
height | weight | smoker | gender | age | color | gender_x | age_x | BMI | |
---|---|---|---|---|---|---|---|---|---|
0 | 189 | 70 | False | 女 | 72 | green | 0 | 69 | 0.001960 |
1 | 153 | 42 | False | 女 | 49 | black | 0 | 46 | 0.001794 |
2 | 178 | 51 | False | 女 | 50 | white | 0 | 47 | 0.001610 |
3 | 170 | 47 | True | 男 | 85 | black | 1 | 82 | 0.001626 |
4 | 163 | 51 | False | 男 | 89 | green | 1 | 86 | 0.001920 |
總結一下對DataFrame的apply操作:
- 當axis=0時,對每列columns執行指定函數;當axis=1時,對每行row執行指定函數。
- 無論axis=0還是axis=1,其傳入指定函數的默認形式均爲Series,可以通過設置raw=True傳入numpy數組。
- 對每個Series執行結果後,會將結果整合在一起返回(若想有返回值,定義函數時需要return相應的值)
- 當然,DataFrame的apply和Series的apply一樣,也能接收更復雜的函數,如傳入參數等,實現原理是一樣的,具體用法詳見官方文檔。
2. applymap
applymap的用法比較簡單,會對DataFrame中的每個單元格執行指定函數的操作。
# 構建一個新的數據集
data2 = pd.DataFrame({
'A':np.random.randn(5),
'B':np.random.randn(5),
'C':np.random.randn(5),
'D':np.random.randn(5)
})
data2
A | B | C | D | |
---|---|---|---|---|
0 | 0.319973 | 0.398420 | -0.421453 | -1.324070 |
1 | 0.507460 | 0.529772 | 0.650397 | 0.645287 |
2 | 0.659481 | -0.858528 | -1.400294 | 1.834288 |
3 | 1.265254 | 1.685537 | 2.031475 | -2.136325 |
4 | -2.616967 | 0.023655 | -0.080487 | -0.694068 |
現在想將DataFrame中所有的值保留兩位小數顯示,使用applymap即可。
data2.applymap(lambda x:"%.2f" % x)
A | B | C | D | |
---|---|---|---|---|
0 | 0.32 | 0.40 | -0.42 | -1.32 |
1 | 0.51 | 0.53 | 0.65 | 0.65 |
2 | 0.66 | -0.86 | -1.40 | 1.83 |
3 | 1.27 | 1.69 | 2.03 | -2.14 |
4 | -2.62 | 0.02 | -0.08 | -0.69 |