8000 字概括精髓,pandas 必知必會 50 例
這是「進擊的 Coder」的第 511 篇技術分享
作者:俊欣
來源:關於數據分析與可視化
本篇我們繼續前面pandas
系列教程的探討,今天小編會介紹pandas庫
當中一些非常基礎的方法與函數,希望大家看了之後會有所收穫。
準備需要的數據集
我們先準備生成一些隨機數,作爲後面需要用到的數據集
index = pd.date_range("1/1/2000", periods=8)
series = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])
df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=["A", "B", "C"])
Head and tail
head()
和tail()
方法是用來查看數據集當中的前幾行和末尾幾行的,默認是查看 5 行,當然讀者朋友也可以自行設定行數
series2 = pd.Series(np.random.randn(100))
series2.head()
output
0 0.733801
1 -0.740149
2 -0.031863
3 2.515542
4 0.615291
dtype: float64
同理
series2.tail()
output
95 -0.526625
96 -0.234975
97 0.744299
98 0.434843
99 -0.609003
dtype: float64
數據的統計分析
在pandas
當中用describe()
方法來對錶格中的數據做一個概括性的統計分析,例如
series2.describe()
output
count 100.000000
mean 0.040813
std 1.003012
min -2.385316
25% -0.627874
50% -0.029732
75% 0.733579
max 2.515542
dtype: float64
當然,我們也可以設置好輸出的分位
series2.describe(percentiles=[0.05, 0.25, 0.75, 0.95])
output
count 100.000000
mean 0.040813
std 1.003012
min -2.385316
5% -1.568183
25% -0.627874
50% -0.029732
75% 0.733579
95% 1.560211
max 2.515542
dtype: float64
對於離散型的數據來說,describe()
方法給出的結果則會簡潔很多
s = pd.Series(["a", "a", "b", "b", "a", "a", "d", "c", "d", "a"])
s.describe()
output
count 10
unique 4
top a
freq 5
dtype: object
要是表格中既包含了離散型數據,也包含了連續型的數據,默認的話,describe()
是會針對連續型數據進行統計分析
df2 = pd.DataFrame({"a": ["Yes", "Yes", "No", "No"], "b": np.random.randn(4)})
df2.describe()
output
b
count 4.000000
mean 0.336053
std 1.398306
min -1.229344
25% -0.643614
50% 0.461329
75% 1.440995
max 1.650898
當然我們也可以指定讓其強制統計分析離散型數據或者連續型數據
df2.describe(include=["object"])
output
a
count 4
unique 2
top Yes
freq 2
同理,我們也可以指定連續型的數據進行統計分析
df2.describe(include=["number"])
output
b
count 4.000000
mean -0.593695
std 0.686618
min -1.538640
25% -0.818440
50% -0.459147
75% -0.234401
max 0.082155
如果我們都要去做統計分析,可以這麼來執行
df2.describe(include="all")
output
a b
count 4 4.000000
unique 2 NaN
top Yes NaN
freq 2 NaN
mean NaN 0.292523
std NaN 1.523908
min NaN -1.906221
25% NaN -0.113774
50% NaN 0.789560
75% NaN 1.195858
max NaN 1.497193
最大 / 最小值的位置
idxmin()
和idxmax()
方法是用來查找表格當中最大 / 最小值的位置,返回的是值的索引
s1 = pd.Series(np.random.randn(5))
s1
output
s1.idxmin(), s1.idxmax()
output
(0, 3)
用在DataFrame
上面的話,如下
df1 = pd.DataFrame(np.random.randn(5, 3), columns=["A", "B", "C"])
df1.idxmin(axis=0)
output
A 4
B 2
C 1
dtype: int64
同理,我們將axis
參數改成1
df1.idxmin(axis=1)
output
0 C
1 C
2 C
3 B
4 A
dtype: object
value_counts()
方法
pandas
當中的value_counts()
方法主要用於數據表的計數以及排序,用來查看錶格當中,指定列有多少個不同的數據值並且計算不同值在該列當中出現的次數,先來看一個簡單的例子
df = pd.DataFrame({'城市': ['北京', '廣州', '上海', '上海', '杭州', '成都', '香港', '南京', '北京', '北京'],
'收入': [10000, 10000, 5500, 5500, 4000, 50000, 8000, 5000, 5200, 5600],
'年齡': [50, 43, 34, 40, 25, 25, 45, 32, 25, 25]})
df["城市"].value_counts()
output
北京 3
上海 2
廣州 1
杭州 1
成都 1
香港 1
南京 1
Name: 城市, dtype: int64
可以看到北京
出現了 3 次,上海
出現了 2 次,並且默認採用的是降序來排列的,下面我們來看一下用升序的方式來排列一下收入
這一列
df["收入"].value_counts(ascending=True)
output
4000 1
50000 1
8000 1
5000 1
5200 1
5600 1
10000 2
5500 2
Name: 收入, dtype: int64
同時裏面也還可以利用參數normalize=True
,來計算不同值的計數佔比
df['年齡'].value_counts(ascending=True,normalize=True)
output
50 0.1
43 0.1
34 0.1
40 0.1
45 0.1
32 0.1
25 0.4
Name: 年齡, dtype: float64
數據分組
我們可以使用cut()
方法以及qcut()
方法來對錶格中的連續型數據分組,首先我們看一下cut()
方法,假設下面這組數據代表的是小組每個成員的年齡
ages = np.array([2,3,10,40,36,45,58,62,85,89,95,18,20,25,35,32])
pd.cut(ages, 5)
output
[(1.907, 20.6], (1.907, 20.6], (1.907, 20.6], (39.2, 57.8], (20.6, 39.2], ..., (1.907, 20.6], (1.907, 20.6], (20.6, 39.2], (20.6, 39.2], (20.6, 39.2]]
Length: 16
Categories (5, interval[float64, right]): [(1.907, 20.6] < (20.6, 39.2] < (39.2, 57.8] <
(57.8, 76.4] < (76.4, 95.0]]
由上可以看到用cut()
方法將數據平分成了 5 個區間,且區間兩邊都有擴展以包含最大值和最小值,當然我們也可以給每一個區間加上標記
pd.cut(ages, 5, labels=[u"嬰兒",u"少年",u"青年",u"中年",u"老年"])
output
['嬰兒', '嬰兒', '嬰兒', '青年', '少年', ..., '嬰兒', '嬰兒', '少年', '少年', '少年']
Length: 16
Categories (5, object): ['嬰兒' < '少年' < '青年' < '中年' < '老年']
而對於qcut()
方法來說,我們可以指定區間來進行分組,例如
pd.qcut(ages, [0,0.5,1], labels=['小朋友','大孩子'])
output
['小朋友', '小朋友', '小朋友', '大孩子', '大孩子', ..., '小朋友', '小朋友', '小朋友', '小朋友', '小朋友']
Length: 16
Categories (2, object): ['小朋友' < '大孩子']
這裏將年齡這組數據分成兩部分 [0, 0.5, 1],一組是標上標記小朋友
,另一組是大孩子
,不過通常情況下,我們用的cut()
方法比較多
引用函數
要是在表格當中引用其他的方法,或者是自建的函數,可以使用通過pandas
當中的以下這幾個方法
-
pipe()
-
apply()
和applymap()
-
agg()
和transform()
pipe()
方法
首先我們來看pipe()
這個方法,我們可以將自己定義好的函數,以鏈路的形式一個接着一個傳給我們要處理的數據集上
def extract_city_name(df):
df["state_name"] = df["state_and_code"].str.split(",").str.get(0)
return df
def add_country_name(df, country_name=None):
df["state_and_country"] = df["state_name"] + country_name
return df
然後我們用pip()
這個方法來將上面我們定義的函數串聯起來
df_p = pd.DataFrame({"city_and_code": ["Arizona, AZ"]})
df_p = pd.DataFrame({"state_and_code": ["Arizona, AZ"]})
df_p.pipe(extract_city_name).pipe(add_country_name, country_)
output
state_and_code state_name state_and_country
0 Arizona, AZ Arizona Arizona_USA
apply()
方法和applymap()
方法
apply()
方法可以對錶格中的數據按照行或者是列方向進行處理,默認是按照列方向,如下
df.apply(np.mean)
output
A -0.101751
B -0.360288
C -0.637433
dtype: float64
當然,我們也可以通過axis
參數來進行調節
df.apply(np.mean, axis = 1)
output
0 -0.803675
1 -0.179640
2 -1.200973
3 0.156888
4 0.381631
5 0.049274
6 1.174923
7 0.612591
dtype: float64
除此之外,我們也可以直接調用匿名函數lambda
的形式
df.apply(lambda x: x.max() - x.min())
output
A 1.922863
B 2.874672
C 1.943930
dtype: float64
也可以調用自己定義的函數方法
df = pd.DataFrame(np.random.randn(5, 3), columns=["A", "B", "C"])
def normalize(x):
return (x - x.mean()) / x.std()
我們用上apply()
方法
df.apply(normalize)
output
A B C
0 1.149795 0.390263 -0.813770
1 0.805843 -0.532374 0.859627
2 0.047824 -0.085334 -0.067179
3 -0.903319 -1.215023 1.149538
4 -1.100144 1.442467 -1.128216
apply()
方法作用於數據集當中的每個行或者是列,而applymap()
方法則是對數據集當中的所有元素都進行處理
df = pd.DataFrame({'key1' : ['a', 'c', 'b', 'b', 'd'],
'key2' : ['one', 'two', 'three', 'two', 'one'],
'data1' : np.arange(1, 6),
'data2' : np.arange(10,15)})
output
key1 key2 data1 data2
0 a one 1 10
1 c two 2 11
2 b three 3 12
3 b four 4 13
4 d five 5 14
我們來自定義一個函數
def add_A(x):
return "A" + str(x)
df.applymap(add_A)
output
key1 key2 data1 data2
0 Aa Aone A1 A10
1 Ac Atwo A2 A11
2 Ab Athree A3 A12
3 Ab Afour A4 A13
4 Ad Afive A5 A14
我們然後也可以通過lambda()
自定義函數方法,然後來去除掉這個A
df.applymap(add_A).applymap(lambda x: x.split("A")[1])
output
key1 key2 data1 data2
0 a one 1 10
1 c two 2 11
2 b three 3 12
3 b four 4 13
4 d five 5 14
agg()
方法和transform()
方法
agg()
方法本意上是聚合函數,我們可以將用於統計分析的一系列方法都放置其中,並且放置多個
df = pd.DataFrame(np.random.randn(5, 3), columns=["A", "B", "C"])
df.agg(np.sum)
output
A 0.178156
B 3.233845
C -0.859622
dtype: float64
當然,當中的np.sum
部分也可以用字符串來表示,例如
df.agg("sum")
output
A -0.606484
B -1.491742
C -1.732083
dtype: float64
我們嘗試在當中放置多個統計分析的函數方法
df.agg(["sum", "mean", "median"])
output
A B C
sum 1.964847 3.855801 0.630042
mean 0.392969 0.771160 0.126008
median 0.821005 0.714804 -0.273685
當然我們也可以和lambda
匿名函數混合着搭配
df.agg(["sum", lambda x: x.mean()])
output
A B C
sum -0.066486 -1.288341 -1.236244
<lambda> -0.013297 -0.257668 -0.247249
或者和自己定義的函數方法混合着用
def my_mean(x):
return x.mean()
df.agg(["sum", my_mean])
output
A B C
sum -4.850201 -1.544773 0.429007
my_mean -0.970040 -0.308955 0.085801
與此同時,我們在agg()
方法中添加字典,實現不同的列使用不同的函數方法
df.agg({"A": "sum", "B": "mean"})
output
A -0.801753
B 0.097550
dtype: float64
例如
df.agg({"A": ["sum", "min"], "B": "mean"})
output
A B
sum 0.911243 NaN
min -0.720225 NaN
mean NaN 0.373411
而當數據集當中既有連續型變量,又有離散型變量的時候,用agg()
方法則就會弄巧成拙了
df = pd.DataFrame(
{
"A": [1, 2, 3],
"B": [1.0, 2.0, 3.0],
"C": ["test1", "test2", "test3"],
"D": pd.date_range("20211101", periods=3),
}
)
df.agg(["min", "sum"])
output
A B C D
min 1 1.0 test1 2021-11-01
sum 6 6.0 test1test2test3 NaT
出來的結果可能並非是用戶所想要的了,而至於transform()
方法,其效果和用法都和agg()
方法及其的相似,這邊也就不多做贅述
索引和列名的重命名
針對索引和列名的重命名,我們可以通過pandas
當中的rename()
方法來實現,例如我們有這樣一個數據集
df1 = pd.DataFrame(np.random.randn(5, 3), columns=["A", "B", "C"],
index = ["a", "b", "c", "d", "e"])
output
A B C
a 0.343690 0.869984 -1.929814
b 1.025613 0.470155 -0.242463
c -0.400908 -0.362684 0.226857
d -1.339706 -0.302005 -1.784452
e -0.957026 -0.813600 0.215098
我們可以這樣來操作
df1.rename(columns={"A": "one", "B": "two", "C": "three"},
index={"a": "apple", "b": "banana", "c": "cat"})
output
one two three
apple 0.383813 0.588964 -0.162386
banana -0.462068 -2.938896 0.935492
cat -0.059807 -1.987281 0.095432
d -0.085230 2.013733 -1.324039
e -0.678352 0.306776 0.808697
當然我們可以拆開來,單獨對行或者是列進行重命名,對列的重命名可以這麼來做
df1.rename({"A": "one", "B": "two", "C": "three"}, axis = "columns")
output
one two three
a -0.997108 -1.383011 0.474298
b 1.009910 0.286303 1.120783
c 1.130700 -0.566922 1.841451
d -0.350438 -0.171079 -0.079804
e 0.988050 -0.524604 0.653306
對行的重命名則可以這麼來做
df1.rename({"a": "apple", "b": "banana", "c": "cat"}, axis = "index")
output
A B C
apple 0.590589 -0.311803 -0.782117
banana 1.528043 -0.944476 -0.337584
cat 1.326057 -0.087368 0.041444
d 1.079768 -0.098314 -0.210999
e 1.654869 1.170333 0.506194
排序
在pandas
當中,我們可以針對數據集當中的值來進行排序
df1 = pd.DataFrame(
{"one": [2, 1, 1, 1], "two": [1, 3, 2, 4], "three": [5, 4, 3, 2]}
)
output
one two three
0 2 1 5
1 1 3 4
2 1 2 3
3 1 4 2
我們按照 “three” 這一列當中的數值來進行排序
df1.sort_values(by = "three")
output
one two three
3 1 4 2
2 1 2 3
1 1 3 4
0 2 1 5
我們也可以依照多列進行排序
df1.sort_values(by = ["one", "two"])
output
one two three
2 1 2 3
1 1 3 4
3 1 4 2
0 2 1 5
在 “one” 這一列相等的時候,比較 “two” 這一列數值的大小,在排序的過程當中,默認採用的都是升序,我們可以改成降序來進行編排
df1.sort_values("two", ascending=False)
output
one two three
3 1 4 2
1 1 3 4
2 1 2 3
0 2 1 5
數據類型的轉換
最後涉及到的是數據類型的轉換,在這之前,我們先得知道如何來查看數據的類型,pandas
當中有相應的方法可以處理
df2 = pd.DataFrame(
{
"A": pd.Series(np.random.randn(5), dtype="float16"),
"B": pd.Series(np.random.randn(5)),
"C": pd.Series(np.array(np.random.randn(5), dtype="uint8")),
}
)
output
A B C
0 -0.498779 -0.501512 0
1 -0.055817 -0.528227 254
2 -0.914551 0.763298 1
3 -0.916016 1.366833 0
4 1.993164 1.834457 0
我們通過dtypes
屬性來查看數據的類型
A float16
B float64
C uint8
dtype: object
而通過astype()
方法來實現數據類型的轉換
df2["B"].astype("int64")
output
0 0
1 0
2 0
3 2
4 1
Name: B, dtype: int64
根據數據類型來篩選
與此同時,我們也可以根據相對應的數據類型來進行篩選,運用pandas
當中的select_dtypes
方法,我們先來創建一個數據集包含了各種數據類型的
df = pd.DataFrame(
{
"string_1": list("abcde"),
"int64_1": list(range(1, 6)),
"uint8_1": np.arange(3, 8).astype("u1"),
"float64_1": np.arange(4.0, 9.0),
"bool1": [True, False, True, True, False],
"bool2": [False, True, False, False, True],
"dates_1": pd.date_range("now", periods=5),
"category_1": pd.Series(list("ABCDE")).astype("category"),
}
)
output
string_1 int64_1 uint8_1 ... bool2 dates_1 category_1
0 a 1 3 ... False 2021-11-10 10:43:05.957685 A
1 b 2 4 ... True 2021-11-11 10:43:05.957685 B
2 c 3 5 ... False 2021-11-12 10:43:05.957685 C
3 d 4 6 ... False 2021-11-13 10:43:05.957685 D
4 e 5 7 ... True 2021-11-14 10:43:05.957685 E
我們先來查看一下各個列的數據類型
df.dtypes
output
string_1 object
int64_1 int64
uint8_1 uint8
float64_1 float64
bool1 bool
bool2 bool
dates_1 datetime64[ns]
category_1 category
dtype: object
我們篩選類型爲布爾值的數據
df.select_dtypes(include=[bool])
output
bool1 bool2
0 True False
1 False True
2 True False
3 True False
4 False True
篩選出數據類型爲整型的數據
df.select_dtypes(include=['int64'])
output
int64_1
0 1
1 2
2 3
3 4
4 5
看完記得關注 @進擊的 Coder
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/7z-2d7JPDyounG9fttBMrQ