Pandas基本功能詳解 | 輕鬆玩轉Pandas(2)

王偉同學發表於2018-07-21

教你學會 Pandas 不是我的目的,教你輕鬆玩轉 Pandas 才是我的目的。我會通過一系列例項來帶入 Pandas 的知識點,讓你在學習 Pandas 的路上不再枯燥。

宣告:我所寫的輕鬆玩轉 Pandas 教程都是免費的,如果對你有幫助,你可以持續關注我。

Pandas基本功能詳解 | 輕鬆玩轉Pandas(2)

Pandas資料結構詳解 | 輕鬆玩轉Pandas(1) 介紹了 Pandas 中常用的兩種資料結構 Series 以及 DataFrame,這裡來看下這些資料結構都有哪些常用的功能。

# 匯入相關庫
import numpy as np
import pandas as pd
複製程式碼

常用的基本功能

當我們構建好了 Series 和 DataFrame 之後,我們會經常使用哪些功能呢?來跟我看看吧。引用上一章節中的場景,我們有一些使用者的的資訊,並將它們儲存到了 DataFrame 中。

因為大多數情況下 DataFrame 比 Series 更為常用,所以這裡以 DataFrame 舉例說明,但實際上很多常用功能對於 Series 也適用。

index = pd.Index(data=["Tom", "Bob", "Mary", "James"], name="name")

data = {
    "age": [18, 30, 25, 40],
    "city": ["BeiJing", "ShangHai", "GuangZhou", "ShenZhen"],
    "sex": ["male", "male", "female", "male"]
}

user_info = pd.DataFrame(data=data, index=index)
user_info
複製程式碼
age city sex
name
Tom 18 BeiJing male
Bob 30 ShangHai male
Mary 25 GuangZhou female
James 40 ShenZhen male

一般拿到資料,我們第一步需要做的是瞭解下資料的整體情況,可以使用 info 方法來檢視。

user_info.info()
複製程式碼
Index: 4 entries, Tom to James
Data columns (total 3 columns):
age     4 non-null int64
city    4 non-null object
sex     4 non-null object
dtypes: int64(1), object(2)
memory usage: 128.0+ bytes
複製程式碼

如果我們的資料量非常大,我想看看資料長啥樣,我當然不希望檢視所有的資料了,這時候我們可以採用只看頭部的 n 條或者尾部的 n 條。檢視頭部的 n 條資料可以使用 head 方法,檢視尾部的 n 條資料可以使用 tail 方法。

user_info.head(2)
複製程式碼
age city sex
name
Tom 18 BeiJing male
Bob 30 ShangHai male

此外,Pandas 中的資料結構都有 ndarray 中的常用方法和屬性,如通過 .shape 獲取資料的形狀,通過 .T 獲取資料的轉置。

user_info.shape
複製程式碼
(4, 3)
複製程式碼
user_info.T
複製程式碼
name Tom Bob Mary James
age 18 30 25 40
city BeiJing ShangHai GuangZhou ShenZhen
sex male male female male

如果我們想要通過 DataFrame 來獲取它包含的原有資料,可以通過 .values 來獲取,獲取後的資料型別其實是一個 ndarray。

user_info.values
複製程式碼
array([[18, 'BeiJing', 'male'],
       [30, 'ShangHai', 'male'],
       [25, 'GuangZhou', 'female'],
       [40, 'ShenZhen', 'male']], dtype=object)
複製程式碼

描述與統計

有時候我們獲取到資料之後,想要檢視下資料的簡單統計指標(最大值、最小值、平均值、中位數等),比如想要檢視年齡的最大值,如何實現呢?

直接對 age 這一列呼叫 max方法即可。

user_info.age.max()
複製程式碼
40
複製程式碼

類似的,通過呼叫 minmeanquantilesum 方法可以實現最小值、平均值、中位數以及求和。可以看到,對一個 Series 呼叫 這幾個方法之後,返回的都只是一個聚合結果。

來介紹個有意思的方法:cumsum,看名字就發現它和 sum 方法有關係,事實上確實如此,cumsum 也是用來求和的,不過它是用來累加求和的,也就是說它得到的結果與原始的 SeriesDataFrame 大小相同。

user_info.age.cumsum()
複製程式碼
name
Tom       18
Bob       48
Mary      73
James    113
Name: age, dtype: int64
複製程式碼

可以看到,cummax 最後的結果就是將上一次求和的結果與原始當前值求和作為當前值。這話聽起來有點繞。舉個例子,上面的 73 = 48 + 25cumsum 也可以用來操作字串型別的物件。

user_info.sex.cumsum()
複製程式碼
name
Tom                    male
Bob                malemale
Mary         malemalefemale
James    malemalefemalemale
Name: sex, dtype: object
複製程式碼

如果想要獲取更多的統計方法,可以參見官方連結:Descriptive statistics

雖然說常見的各種統計值都有對應的方法,如果我想要得到多個指標的話,就需要呼叫多次方法,是不是顯得有點麻煩呢?

Pandas 設計者自然也考慮到了這個問題,想要一次性獲取多個統計指標,只需呼叫 describe 方法即可

user_info.describe()
複製程式碼
age
count 4.000000
mean 28.250000
std 9.251126
min 18.000000
25% 23.250000
50% 27.500000
75% 32.500000
max 40.000000

可以看到,直接呼叫 describe 方法後,會顯示出數字型別的列的一些統計指標,如 總數、平均數、標準差、最小值、最大值、25%/50%/75% 分位數。如果想要檢視非數字型別的列的統計指標,可以設定 include=["object"] 來獲得。

user_info.describe(include=["object"])
複製程式碼
city sex
count 4 4
unique 4 2
top BeiJing male
freq 1 3

上面的結果展示了非數字型別的列的一些統計指標:總數,去重後的個數、最常見的值、最常見的值的頻數。

此外,如果我想要統計下某列中每個值出現的次數,如何快速實現呢?呼叫 value_counts 方法快速獲取 Series 中每個值出現的次數。

user_info.sex.value_counts()
複製程式碼
male      3
female    1
Name: sex, dtype: int64
複製程式碼

如果想要獲取某列最大值或最小值對應的索引,可以使用 idxmaxidxmin 方法完成。

user_info.age.idxmax()
複製程式碼
'James'
複製程式碼

離散化

有時候,我們會碰到這樣的需求,想要將年齡進行離散化(分桶),直白來說就是將年齡分成幾個區間,這裡我們想要將年齡分成 3 個區間段。就可以使用 Pandas 的 cut 方法來完成。

pd.cut(user_info.age, 3)
複製程式碼
name
Tom      (17.978, 25.333]
Bob      (25.333, 32.667]
Mary     (17.978, 25.333]
James      (32.667, 40.0]
Name: age, dtype: category
Categories (3, interval[float64]): [(17.978, 25.333] < (25.333, 32.667] < (32.667, 40.0]]
複製程式碼

可以看到, cut 自動生成了等距的離散區間,如果自己想定義也是沒問題的。

pd.cut(user_info.age, [1, 18, 30, 50])
複製程式碼
name
Tom       (1, 18]
Bob      (18, 30]
Mary     (18, 30]
James    (30, 50]
Name: age, dtype: category
Categories (3, interval[int64]): [(1, 18] < (18, 30] < (30, 50]]
複製程式碼

有時候離散化之後,想要給每個區間起個名字,可以指定 labels 引數。

pd.cut(user_info.age, [1, 18, 30, 50], labels=["childhood", "youth", "middle"])
複製程式碼
name
Tom      childhood
Bob          youth
Mary         youth
James       middle
Name: age, dtype: category
Categories (3, object): [childhood < youth < middle]
複製程式碼

除了可以使用 cut 進行離散化之外,qcut 也可以實現離散化。cut 是根據每個值的大小來進行離散化的,qcut 是根據每個值出現的次數來進行離散化的。

pd.qcut(user_info.age, 3)
複製程式碼
name
Tom      (17.999, 25.0]
Bob        (25.0, 30.0]
Mary     (17.999, 25.0]
James      (30.0, 40.0]
Name: age, dtype: category
Categories (3, interval[float64]): [(17.999, 25.0] < (25.0, 30.0] < (30.0, 40.0]]
複製程式碼

排序功能

在進行資料分析時,少不了進行資料排序。Pandas 支援兩種排序方式:按軸(索引或列)排序和按實際值排序。

先來看下按索引排序:sort_index 方法預設是按照索引進行正序排的。

user_info.sort_index()
複製程式碼
age city sex
name
Bob 30 ShangHai male
James 40 ShenZhen male
Mary 25 GuangZhou female
Tom 18 BeiJing male

如果想要按照列進行倒序排,可以設定引數 axis=1ascending=False

user_info.sort_index(axis=1, ascending=False)
複製程式碼
sex city age
name
Tom male BeiJing 18
Bob male ShangHai 30
Mary female GuangZhou 25
James male ShenZhen 40

如果想要實現按照實際值來排序,例如想要按照年齡排序,如何實現呢?

使用 sort_values 方法,設定引數 by="age" 即可。

user_info.sort_values(by="age")
複製程式碼
age city sex
name
Tom 18 BeiJing male
Mary 25 GuangZhou female
Bob 30 ShangHai male
James 40 ShenZhen male

有時候我們可能需要按照多個值來排序,例如:按照年齡和城市來一起排序,可以設定引數 by 為一個 list 即可。

注意:list 中每個元素的順序會影響排序優先順序的

user_info.sort_values(by=["age", "city"])
複製程式碼
age city sex
name
Tom 18 BeiJing male
Mary 25 GuangZhou female
Bob 30 ShangHai male
James 40 ShenZhen male

一般在排序後,我們可能需要獲取最大的n個值或最小值的n個值,我們可以使用 nlargestnsmallest 方法來完成,這比先進行排序,再使用 head(n) 方法快得多。

user_info.age.nlargest(2)
複製程式碼
name
James    40
Bob      30
Name: age, dtype: int64
複製程式碼

函式應用

雖說 Pandas 為我們提供了非常豐富的函式,有時候我們可能需要自己定製一些函式,並將它應用到 DataFrame 或 Series。常用到的函式有:mapapplyapplymap

map 是 Series 中特有的方法,通過它可以對 Series 中的每個元素實現轉換。

如果我想通過年齡判斷使用者是否屬於中年人(30歲以上為中年),通過 map 可以輕鬆搞定它。

# 接收一個 lambda 函式
user_info.age.map(lambda x: "yes" if x >= 30 else "no")
複製程式碼
name
Tom       no
Bob      yes
Mary      no
James    yes
Name: age, dtype: object
複製程式碼

又比如,我想要通過城市來判斷是南方還是北方,我可以這樣操作。

city_map = {
    "BeiJing": "north",
    "ShangHai": "south",
    "GuangZhou": "south",
    "ShenZhen": "south"
}

# 傳入一個 map
user_info.city.map(city_map)
複製程式碼
name
Tom      north
Bob      south
Mary     south
James    south
Name: city, dtype: object
複製程式碼

apply 方法既支援 Series,也支援 DataFrame,在對 Series 操作時會作用到每個值上,在對 DataFrame 操作時會作用到所有行或所有列(通過 axis 引數控制)。

# 對 Series 來說,apply 方法 與 map 方法區別不大。
user_info.age.apply(lambda x: "yes" if x >= 30 else "no")
複製程式碼
name
Tom       no
Bob      yes
Mary      no
James    yes
Name: age, dtype: object
複製程式碼
# 對 DataFrame 來說,apply 方法的作用物件是一行或一列資料(一個Series)
user_info.apply(lambda x: x.max(), axis=0)
複製程式碼
age           40
city    ShenZhen
sex         male
dtype: object
複製程式碼

applymap 方法針對於 DataFrame,它作用於 DataFrame 中的每個元素,它對 DataFrame 的效果類似於 apply 對 Series 的效果。

user_info.applymap(lambda x: str(x).lower())
複製程式碼
age city sex
name
Tom 18 beijing male
Bob 30 shanghai male
Mary 25 guangzhou female
James 40 shenzhen male

修改列/索引名稱

在使用 DataFrame 的過程中,經常會遇到修改列名,索引名等情況。使用 rename 輕鬆可以實現。

修改列名只需要設定引數 columns 即可。

user_info.rename(columns={"age": "Age", "city": "City", "sex": "Sex"})
複製程式碼
Age City Sex
name
Tom 18 BeiJing male
Bob 30 ShangHai male
Mary 25 GuangZhou female
James 40 ShenZhen male

類似的,修改索引名只需要設定引數 index 即可。

user_info.rename(index={"Tom": "tom", "Bob": "bob"})
複製程式碼
age city sex
name
tom 18 BeiJing male
bob 30 ShangHai male
Mary 25 GuangZhou female
James 40 ShenZhen male

型別操作

如果想要獲取每種型別的列數的話,可以使用 get_dtype_counts 方法。

user_info.get_dtype_counts()
複製程式碼
int64     1
object    2
dtype: int64
複製程式碼

如果想要轉換資料型別的話,可以通過 astype 來完成。

user_info["age"].astype(float)
複製程式碼
name
Tom      18.0
Bob      30.0
Mary     25.0
James    40.0
Name: age, dtype: float64
複製程式碼

有時候會涉及到將 object 型別轉為其他型別,常見的有轉為數字、日期、時間差,Pandas 中分別對應 to_numericto_datetimeto_timedelta 方法。

這裡給這些使用者都新增一些關於身高的資訊。

user_info["height"] = ["178", "168", "178", "180cm"]
user_info
複製程式碼
age city sex height
name
Tom 18 BeiJing male 178
Bob 30 ShangHai male 168
Mary 25 GuangZhou female 178
James 40 ShenZhen male 180cm

現在將身高這一列轉為數字,很明顯,180cm 並非數字,為了強制轉換,我們可以傳入 errors 引數,這個引數的作用是當強轉失敗時的處理方式。

預設情況下,errors='raise',這意味著強轉失敗後直接丟擲異常,設定 errors='coerce' 可以在強轉失敗時將有問題的元素賦值為 pd.NaT(對於datetime和timedelta)或 np.nan(數字)。設定 errors='ignore' 可以在強轉失敗時返回原有的資料。

pd.to_numeric(user_info.height, errors="coerce")
複製程式碼
name
Tom      178.0
Bob      168.0
Mary     178.0
James      NaN
Name: height, dtype: float64
複製程式碼
pd.to_numeric(user_info.height, errors="ignore")
複製程式碼
name
Tom        178
Bob        168
Mary       178
James    180cm
Name: height, dtype: object
複製程式碼

想要學習更多關於人工智慧的知識,請關注公眾號:AI派

qrcode_for_gh_60cef389e81c_258.jpg

這裡我將整篇文章的內容整理成了pdf,想要pdf檔案的可以在公眾號後臺回覆關鍵字:pandas02

更多Pandas知識見:輕鬆玩轉Pandas

相關文章