來源:https://datawhalechina.github.io/joyful-pandas/build/html/目錄/ch3.html
In [1]: import numpy as np
In [2]: import pandas as pd
一、索引器
1. 表的列索引
列索引是最常見的索引形式斋扰,一般通過 []
來實(shí)現(xiàn)。通過 [列名]
可以從 DataFrame
中取出相應(yīng)的列雷袋,返回值為 Series
荧降,例如從表中取出姓名一列:
In [3]: df = pd.read_csv('data/learn_pandas.csv',
...: usecols = ['School', 'Grade', 'Name', 'Gender',
...: 'Weight', 'Transfer'])
...:
In [4]: df['Name'].head()
Out[4]:
0 Gaopeng Yang
1 Changqiang You
2 Mei Sun
3 Xiaojuan Sun
4 Gaojuan You
Name: Name, dtype: object
如果要取出多個(gè)列接箫,則可以通過 [列名組成的列表]
,其返回值為一個(gè) DataFrame
朵诫,例如從表中取出性別和姓名兩列:
In [5]: df[['Gender', 'Name']].head()
Out[5]:
Gender Name
0 Female Gaopeng Yang
1 Male Changqiang You
2 Male Mei Sun
3 Female Xiaojuan Sun
4 Male Gaojuan You
此外辛友,若要取出單列,且列名中不包含空格剪返,則可以用 .列名
取出废累,這和 [列名]
是等價(jià)的:
In [6]: df.Name.head()
Out[6]:
0 Gaopeng Yang
1 Changqiang You
2 Mei Sun
3 Xiaojuan Sun
4 Gaojuan You
Name: Name, dtype: object
2. 序列的行索引
【a】以字符串為索引的 Series
如果取出單個(gè)索引的對(duì)應(yīng)元素,則可以使用 [item]
脱盲,若 Series
只有單個(gè)值對(duì)應(yīng)邑滨,則返回這個(gè)標(biāo)量值,如果有多個(gè)值對(duì)應(yīng)钱反,則返回一個(gè) Series
:
In [7]: s = pd.Series([1, 2, 3, 4, 5, 6],
...: index=['a', 'b', 'a', 'a', 'a', 'c'])
...:
In [8]: s['a'] #把對(duì)應(yīng)的所有元素都取出來
Out[8]:
a 1
a 3
a 4
a 5
dtype: int64
In [9]: s['b']
Out[9]: 2
如果取出多個(gè)索引的對(duì)應(yīng)元素掖看,則可以使用 [items的列表]
:
In [10]: s[['c', 'b']]
Out[10]:
c 6
b 2
dtype: int64
如果想要取出某兩個(gè)索引之間的元素,并且這兩個(gè)索引是在整個(gè)索引中唯一出現(xiàn)面哥,則可以使用切片哎壳,同時(shí)需要注意這里的切片會(huì)包含兩個(gè)端點(diǎn):
In [11]: s['c': 'b': -2] #步長(zhǎng)是-2,從后往前取
Out[11]:
c 6
a 4
b 2
dtype: int64
【b】以整數(shù)為索引的 Series
在使用數(shù)據(jù)的讀入函數(shù)時(shí)尚卫,如果不特別指定所對(duì)應(yīng)的列作為索引归榕,那么會(huì)生成從0開始的整數(shù)索引作為默認(rèn)索引。當(dāng)然吱涉,任意一組符合長(zhǎng)度要求的整數(shù)都可以作為索引刹泄。
和字符串一樣,如果使用 [int]
或 [int_list]
怎爵,則可以取出對(duì)應(yīng)索引 元素 的值:
In [12]: s = pd.Series(['a', 'b', 'c', 'd', 'e', 'f'],
....: index=[1, 3, 1, 2, 5, 4])
....:
In [13]: s[1]
Out[13]:
1 a
1 c
dtype: object
In [14]: s[[2,3]]
Out[14]:
2 d
3 b
dtype: object
如果使用整數(shù)切片特石,則會(huì)取出對(duì)應(yīng)索引 位置 的值,注意這里的整數(shù)切片同 Python
中的切片一樣不包含右端點(diǎn):
In [15]: s[1:-1:2]#1和-1是位置鳖链,而不是索引值
Out[15]:
3 b
2 d
dtype: object
關(guān)于索引類型的說明
如果不想陷入麻煩姆蘸,那么請(qǐng)不要把純浮點(diǎn)以及任何混合類型(字符串、整數(shù)撒轮、浮點(diǎn)類型等的混合)作為索引乞旦,否則可能會(huì)在具體的操作時(shí)報(bào)錯(cuò)或者返回非預(yù)期的結(jié)果,并且在實(shí)際的數(shù)據(jù)分析中也不存在這樣做的動(dòng)機(jī)题山。
3. loc索引器
前面講到了對(duì) DataFrame
的列進(jìn)行選取兰粉,下面要討論其行的選取。對(duì)于表而言顶瞳,有兩種索引器:
一種是基于 元素 的
loc
索引器另一種是基于 位置 的
iloc
索引器
loc
索引器的一般形式是 loc[*, *]
玖姑,其中第一個(gè) *
代表行的選擇愕秫,第二個(gè) *
代表列的選擇,如果省略第二個(gè)位置寫作 loc[*]
焰络,這個(gè) *
是指行的篩選戴甩。其中, *
的位置一共有五類合法對(duì)象闪彼,分別是:單個(gè)元素甜孤、元素列表、元素切片畏腕、布爾列表以及函數(shù)缴川,下面將依次說明。
為了演示相應(yīng)操作描馅,先利用 set_index
方法把 Name
列設(shè)為索引把夸,關(guān)于該函數(shù)的其他用法將在多級(jí)索引一章介紹。
In [16]: df_demo = df.set_index('Name')
In [17]: df_demo.head()
Out[17]:
School Grade Gender Weight Transfer
Name
Gaopeng Yang Shanghai Jiao Tong University Freshman Female 46.0 N
Changqiang You Peking University Freshman Male 70.0 N
Mei Sun Shanghai Jiao Tong University Senior Male 89.0 N
Xiaojuan Sun Fudan University Sophomore Female 41.0 N
Gaojuan You Fudan University Sophomore Male 74.0 N
【a】 *
為單個(gè)元素
此時(shí)铭污,直接取出相應(yīng)的行或列恋日,如果該元素在索引中重復(fù)則結(jié)果為 DataFrame
,否則為 Series
:
In [18]: df_demo.loc['Qiang Sun'] # 多個(gè)人叫此名字嘹狞,結(jié)果為 DataFrame
Out[18]:
School Grade Gender Weight Transfer
Name
Qiang Sun Tsinghua University Junior Female 53.0 N
Qiang Sun Tsinghua University Sophomore Female 40.0 N
Qiang Sun Shanghai Jiao Tong University Junior Female NaN N
In [19]: df_demo.loc['Quan Zhao'] # 名字唯一結(jié)果為 Series
Out[19]:
School Shanghai Jiao Tong University
Grade Junior
Gender Female
Weight 53
Transfer N
Name: Quan Zhao, dtype: object
也可以同時(shí)選擇行和列:
In [20]: df_demo.loc['Qiang Sun', 'School'] # 返回Series
Out[20]:
Name
Qiang Sun Tsinghua University
Qiang Sun Tsinghua University
Qiang Sun Shanghai Jiao Tong University
Name: School, dtype: object
In [21]: df_demo.loc['Quan Zhao', 'School'] # 返回單個(gè)元素
Out[21]: 'Shanghai Jiao Tong University'
【b】 *
為元素列表
此時(shí)岂膳,取出列表中所有元素值對(duì)應(yīng)的行或列:
In [22]: df_demo.loc[['Qiang Sun','Quan Zhao'], ['School','Gender']]
Out[22]:
School Gender
Name
Qiang Sun Tsinghua University Female
Qiang Sun Tsinghua University Female
Qiang Sun Shanghai Jiao Tong University Female
Quan Zhao Shanghai Jiao Tong University Female
【c】 *
為切片
之前的 Series
使用字符串索引時(shí)提到,如果是唯一值(必須是唯一的)的起點(diǎn)和終點(diǎn)字符刁绒,那么就可以使用切片闷营,并且包含兩個(gè)端點(diǎn)烤黍,如果不唯一則報(bào)錯(cuò):
In [23]: df_demo.loc['Gaojuan You':'Gaoqiang Qian', 'School':'Gender']
Out[23]:
School Grade Gender
Name
Gaojuan You Fudan University Sophomore Male
Xiaoli Qian Tsinghua University Freshman Female
Qiang Chu Shanghai Jiao Tong University Freshman Female
Gaoqiang Qian Tsinghua University Junior Female
需要注意的是知市,如果 DataFrame
使用整數(shù)索引,其使用整數(shù)切片的時(shí)候和上面字符串索引的要求一致速蕊,都是 元素 切片嫂丙,包含端點(diǎn)且起點(diǎn)、終點(diǎn)不允許有重復(fù)值规哲。
In [24]: df_loc_slice_demo = df_demo.copy()
In [25]: df_loc_slice_demo.index = range(df_demo.shape[0],0,-1)
In [26]: df_loc_slice_demo.loc[5:3]
Out[26]:
School Grade Gender Weight Transfer
5 Fudan University Junior Female 46.0 N
4 Tsinghua University Senior Female 50.0 N
3 Shanghai Jiao Tong University Senior Female 45.0 N
In [27]: df_loc_slice_demo.loc[3:5] # 沒有返回跟啤,說明不是整數(shù)位置切片,因?yàn)樯厦娴乃饕樞蚴?,4,3唉锌,所以沒有索引元素是3到5的區(qū)間
Out[27]:
Empty DataFrame
Columns: [School, Grade, Gender, Weight, Transfer]
Index: []
【d】 *
為布爾列表
在實(shí)際的數(shù)據(jù)處理中隅肥,根據(jù)條件來篩選行是極其常見的,此處傳入 loc
的布爾列表與 DataFrame
長(zhǎng)度相同袄简,且列表為 True
的位置所對(duì)應(yīng)的行會(huì)被選中腥放, False
則會(huì)被剔除。
例如绿语,選出體重超過70kg的學(xué)生:
In [28]: df_demo.loc[df_demo.Weight>70].head()
Out[28]:
School Grade Gender Weight Transfer
Name
Mei Sun Shanghai Jiao Tong University Senior Male 89.0 N
Gaojuan You Fudan University Sophomore Male 74.0 N
Xiaopeng Zhou Shanghai Jiao Tong University Freshman Male 74.0 N
Xiaofeng Sun Tsinghua University Senior Male 71.0 N
Qiang Zheng Shanghai Jiao Tong University Senior Male 87.0 N
前面所提到的傳入元素列表秃症,也可以通過 isin
方法返回的布爾列表等價(jià)寫出候址,例如選出所有大一和大四的同學(xué)信息:
In [29]: df_demo.loc[df_demo.Grade.isin(['Freshman', 'Senior'])].head() #與之前的de_demo.loc['Qiang Sun', 'School']的區(qū)別是不用設(shè)置index了,并且返回的不是series而是dataframe
Out[29]:
School Grade Gender Weight Transfer
Name
Gaopeng Yang Shanghai Jiao Tong University Freshman Female 46.0 N
Changqiang You Peking University Freshman Male 70.0 N
Mei Sun Shanghai Jiao Tong University Senior Male 89.0 N
Xiaoli Qian Tsinghua University Freshman Female 51.0 N
Qiang Chu Shanghai Jiao Tong University Freshman Female 52.0 N
對(duì)于復(fù)合條件而言种柑,可以用 |(或), &(且), ~(取反)
的組合來實(shí)現(xiàn)岗仑,例如選出復(fù)旦大學(xué)中體重超過70kg的大四學(xué)生,或者北大男生中體重超過80kg的非大四的學(xué)生:
In [30]: condition_1_1 = df_demo.School == 'Fudan University'
In [31]: condition_1_2 = df_demo.Grade == 'Senior'
In [32]: condition_1_3 = df_demo.Weight > 70
In [33]: condition_1 = condition_1_1 & condition_1_2 & condition_1_3
In [34]: condition_2_1 = df_demo.School == 'Peking University'
In [35]: condition_2_2 = df_demo.Grade == 'Senior'
In [36]: condition_2_3 = df_demo.Weight > 80
In [37]: condition_2 = condition_2_1 & (~condition_2_2) & condition_2_3
In [38]: df_demo.loc[condition_1 | condition_2]
Out[38]:
School Grade Gender Weight Transfer
Name
Qiang Han Peking University Freshman Male 87.0 N
Chengpeng Zhou Fudan University Senior Male 81.0 N
Changpeng Zhao Peking University Freshman Male 83.0 N
Chengpeng Qian Fudan University Senior Male 73.0 Y
練一練
select_dtypes
是一個(gè)實(shí)用函數(shù)聚请,它能夠從表中選出相應(yīng)類型的列荠雕,若要選出所有數(shù)值型的列,只需使用.select_dtypes('number')
驶赏,請(qǐng)利用布爾列表選擇的方法結(jié)合DataFrame
的dtypes
屬性在learn_pandas
數(shù)據(jù)集上實(shí)現(xiàn)這個(gè)功能舞虱。
df.loc[:,df.dtypes=='float64'] #:可以選擇所有行數(shù)
【e】 *
為函數(shù)
這里的函數(shù),必須以前面的四種合法形式之一為返回值母市,并且函數(shù)的輸入值為 DataFrame
本身矾兜。假設(shè)仍然是上述復(fù)合條件篩選的例子,可以把邏輯寫入一個(gè)函數(shù)中再返回患久,需要注意的是函數(shù)的形式參數(shù) x
本質(zhì)上即為 df_demo
:
In [39]: def condition(x):
....: condition_1_1 = x.School == 'Fudan University'
....: condition_1_2 = x.Grade == 'Senior'
....: condition_1_3 = x.Weight > 70
....: condition_1 = condition_1_1 & condition_1_2 & condition_1_3
....: condition_2_1 = x.School == 'Peking University'
....: condition_2_2 = x.Grade == 'Senior'
....: condition_2_3 = x.Weight > 80
....: condition_2 = condition_2_1 & (~condition_2_2) & condition_2_3
....: result = condition_1 | condition_2
....: return result
....:
In [40]: df_demo.loc[condition]
Out[40]:
School Grade Gender Weight Transfer
Name
Qiang Han Peking University Freshman Male 87.0 N
Chengpeng Zhou Fudan University Senior Male 81.0 N
Changpeng Zhao Peking University Freshman Male 83.0 N
Chengpeng Qian Fudan University Senior Male 73.0 Y
此外椅寺,還支持使用 lambda
表達(dá)式,其返回值也同樣必須是先前提到的四種形式之一:
In [41]: df_demo.loc[lambda x:'Quan Zhao', lambda x:'Gender']
Out[41]: 'Female'
由于函數(shù)無法返回如 start: end: step
的切片形式蒋失,故返回切片時(shí)要用 slice
對(duì)象進(jìn)行包裝:
In [42]: df_demo.loc[lambda x: slice('Gaojuan You', 'Gaoqiang Qian')] #結(jié)果和df_demo.loc['Gaojuan You':'Gaoqiang Qian']效果相同
Out[42]:
School Grade Gender Weight Transfer
Name
Gaojuan You Fudan University Sophomore Male 74.0 N
Xiaoli Qian Tsinghua University Freshman Female 51.0 N
Qiang Chu Shanghai Jiao Tong University Freshman Female 52.0 N
Gaoqiang Qian Tsinghua University Junior Female 50.0 N
最后需要指出的是返帕,對(duì)于 Series
也可以使用 loc
索引,其遵循的原則與 DataFrame
中用于行篩選的 loc[*]
完全一致篙挽,此處不再贅述荆萤。
不要使用鏈?zhǔn)劫x值
在對(duì)表或者序列賦值時(shí),應(yīng)當(dāng)在使用一層索引器后直接進(jìn)行賦值操作铣卡,這樣做是由于進(jìn)行多次索引后賦值是賦在臨時(shí)返回的
copy
副本上的链韭,而沒有真正修改元素從而報(bào)出SettingWithCopyWarning
警告。例如煮落,下面給出的例子:
In [43]: df_chain = pd.DataFrame([[0,0],[1,0],[-1,0]], columns=list('AB'))
In [44]: df_chain
Out[44]:
A B
0 0 0
1 1 0
2 -1 0
In [45]: import warnings
In [46]: with warnings.catch_warnings():
....: warnings.filterwarnings('error')
....: try:
....: df_chain[df_chain.A!=0].B = 1 # 使用方括號(hào)列索引后敞峭,再使用點(diǎn)的列索引
....: except Warning as w:
....: Warning_Msg = w
....:
In [47]: print(Warning_Msg)
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
In [48]: df_chain
Out[48]:
A B
0 0 0
1 1 0
2 -1 0
In [49]: df_chain.loc[df_chain.A!=0,'B'] = 1
In [50]: df_chain
Out[50]:
A B
0 0 0
1 1 1
2 -1 1
4. iloc索引器
iloc
的使用與 loc
完全類似,只不過是針對(duì)位置進(jìn)行篩選蝉仇,在相應(yīng)的 *
位置處一共也有五類合法對(duì)象旋讹,分別是:整數(shù)、整數(shù)列表轿衔、整數(shù)切片沉迹、布爾列表以及函數(shù),函數(shù)的返回值必須是前面的四類合法對(duì)象中的一個(gè)害驹,其輸入同樣也為 DataFrame
本身鞭呕。
注意和loc的切片不同在于,不包含結(jié)束端點(diǎn)
In [51]: df_demo.iloc[1, 1] # 第二行第二列
Out[51]: 'Freshman'
In [52]: df_demo.iloc[[0, 1], [0, 1]] # 前兩行前兩列
Out[52]:
School Grade
Name
Gaopeng Yang Shanghai Jiao Tong University Freshman
Changqiang You Peking University Freshman
In [53]: df_demo.iloc[1: 4, 2:4] # 切片不包含結(jié)束端點(diǎn)
Out[53]:
Gender Weight
Name
Changqiang You Male 70.0
Mei Sun Male 89.0
Xiaojuan Sun Female 41.0
In [54]: df_demo.iloc[lambda x: slice(1, 4)] # 傳入切片為返回值的函數(shù)
Out[54]:
School Grade Gender Weight Transfer
Name
Changqiang You Peking University Freshman Male 70.0 N
Mei Sun Shanghai Jiao Tong University Senior Male 89.0 N
Xiaojuan Sun Fudan University Sophomore Female 41.0 N
在使用布爾列表的時(shí)候要特別注意裙秋,不能傳入 Series
而必須傳入序列的 values
琅拌,否則會(huì)報(bào)錯(cuò)缨伊。因此,在使用布爾篩選的時(shí)候還是應(yīng)當(dāng)優(yōu)先考慮 loc
的方式进宝。
例如刻坊,選出體重超過80kg的學(xué)生:
In [55]: df_demo.iloc[(df_demo.Weight>80).values].head()
Out[55]:
School Grade Gender Weight Transfer
Name
Mei Sun Shanghai Jiao Tong University Senior Male 89.0 N
Qiang Zheng Shanghai Jiao Tong University Senior Male 87.0 N
Qiang Han Peking University Freshman Male 87.0 N
Chengpeng Zhou Fudan University Senior Male 81.0 N
Feng Han Shanghai Jiao Tong University Sophomore Male 82.0 N
對(duì) Series
而言同樣也可以通過 iloc
返回相應(yīng)位置的值或子序列:
In [56]: df_demo.School.iloc[1]
Out[56]: 'Peking University'
In [57]: df_demo.School.iloc[1:5:2]
Out[57]:
Name
Changqiang You Peking University
Xiaojuan Sun Fudan University
Name: School, dtype: object
5. query方法(更方便)
在 pandas
中,支持把字符串形式的查詢表達(dá)式傳入 query
方法來查詢數(shù)據(jù)党晋,其表達(dá)式的執(zhí)行結(jié)果必須返回布爾列表谭胚。在進(jìn)行復(fù)雜索引時(shí),由于這種檢索方式無需像普通方法一樣重復(fù)使用 DataFrame
的名字來引用列名未玻,一般而言會(huì)使代碼長(zhǎng)度在不降低可讀性的前提下有所減少灾而。
例如,將 loc
一節(jié)中的復(fù)合條件查詢例子可以如下改寫:
In [58]: df.query('((School == "Fudan University")&'
....: ' (Grade == "Senior")&'
....: ' (Weight > 70))|'
....: '((School == "Peking University")&'
....: ' (Grade != "Senior")&'
....: ' (Weight > 80))')
....:
Out[58]:
School Grade Name Gender Weight Transfer
38 Peking University Freshman Qiang Han Male 87.0 N
66 Fudan University Senior Chengpeng Zhou Male 81.0 N
99 Peking University Freshman Changpeng Zhao Male 83.0 N
131 Fudan University Senior Chengpeng Qian Male 73.0 Y
在 query
表達(dá)式中扳剿,幫用戶注冊(cè)了所有來自 DataFrame
的列名旁趟,所有屬于該 Series
的方法都可以被調(diào)用,和正常的函數(shù)調(diào)用并沒有區(qū)別庇绽,例如查詢體重超過均值的學(xué)生:
In [59]: df.query('Weight > Weight.mean()').head()
Out[59]:
School Grade Name Gender Weight Transfer
1 Peking University Freshman Changqiang You Male 70.0 N
2 Shanghai Jiao Tong University Senior Mei Sun Male 89.0 N
4 Fudan University Sophomore Gaojuan You Male 74.0 N
10 Shanghai Jiao Tong University Freshman Xiaopeng Zhou Male 74.0 N
14 Tsinghua University Senior Xiaomei Zhou Female 57.0 N
query中引用帶空格的列名
對(duì)于含有空格的列名锡搜,需要使用 `col name` 的方式進(jìn)行引用。
同時(shí)瞧掺,在 query
中還注冊(cè)了若干英語的字面用法耕餐,幫助提高可讀性,例如: or, and,or, is in, not in
辟狈。例如肠缔,篩選出男生中不是大一大二的學(xué)生:
In [60]: df.query('(Grade not in ["Freshman", "Sophomore"]) and'
....: '(Gender == "Male")').head()
....:
Out[60]:
School Grade Name Gender Weight Transfer
2 Shanghai Jiao Tong University Senior Mei Sun Male 89.0 N
16 Tsinghua University Junior Xiaoqiang Qin Male 68.0 N
17 Tsinghua University Junior Peng Wang Male 65.0 N
18 Tsinghua University Senior Xiaofeng Sun Male 71.0 N
21 Shanghai Jiao Tong University Senior Xiaopeng Shen Male 62.0 NaN
此外,在字符串中出現(xiàn)與列表的比較時(shí)哼转, ==
和 !=
分別表示元素出現(xiàn)在列表和沒有出現(xiàn)在列表明未,等價(jià)于 is in
和 not in
,例如查詢所有大三和大四的學(xué)生:
In [61]: df.query('Grade == ["Junior", "Senior"]').head()
Out[61]:
School Grade Name Gender Weight Transfer
2 Shanghai Jiao Tong University Senior Mei Sun Male 89.0 N
7 Tsinghua University Junior Gaoqiang Qian Female 50.0 N
9 Peking University Junior Juan Xu Female NaN N
11 Tsinghua University Junior Xiaoquan Lv Female 43.0 N
12 Shanghai Jiao Tong University Senior Peng You Female 48.0 NaN
對(duì)于 query
中的字符串释簿,如果要引用外部變量亚隅,只需在變量名前加 @
符號(hào)硼莽。例如庶溶,取出體重位于70kg到80kg之間的學(xué)生:
In [62]: low, high =70, 80 #外部變量
In [63]: df.query('Weight.between(@low, @high)').head()
Out[63]:
School Grade Name Gender Weight Transfer
1 Peking University Freshman Changqiang You Male 70.0 N
4 Fudan University Sophomore Gaojuan You Male 74.0 N
10 Shanghai Jiao Tong University Freshman Xiaopeng Zhou Male 74.0 N
18 Tsinghua University Senior Xiaofeng Sun Male 71.0 N
35 Peking University Freshman Gaoli Zhao Male 78.0 N
6. 隨機(jī)抽樣sample
如果把 DataFrame
的每一行看作一個(gè)樣本,或把每一列看作一個(gè)特征懂鸵,再把整個(gè) DataFrame
看作總體偏螺,想要對(duì)樣本或特征進(jìn)行隨機(jī)抽樣就可以用 sample
函數(shù)。有時(shí)在拿到大型數(shù)據(jù)集后匆光,想要對(duì)統(tǒng)計(jì)特征進(jìn)行計(jì)算來了解數(shù)據(jù)的大致分布套像,但是這很費(fèi)時(shí)間。同時(shí)终息,由于許多統(tǒng)計(jì)特征在等概率不放回的簡(jiǎn)單隨機(jī)抽樣條件下夺巩,是總體統(tǒng)計(jì)特征的無偏估計(jì)贞让,比如樣本均值和總體均值,那么就可以先從整張表中抽出一部分來做近似估計(jì)柳譬。
sample
函數(shù)中的主要參數(shù)為 n, axis, frac, replace, weights
喳张,前三個(gè)分別是指抽樣數(shù)量、抽樣的方向(0為行美澳、1為列)和抽樣比例(0.3則為從總體中抽出30%的樣本)销部。
replace
和 weights
分別是指是否放回和每個(gè)樣本的抽樣相對(duì)概率,當(dāng) replace = True
則表示有放回抽樣制跟。例如舅桩,對(duì)下面構(gòu)造的 df_sample
以 value
值的相對(duì)大小為抽樣概率進(jìn)行有放回抽樣,抽樣數(shù)量為3雨膨。
In [64]: df_sample = pd.DataFrame({'id': list('abcde'),
....: 'value': [1, 2, 3, 4, 90]})
....:
In [65]: df_sample
Out[65]:
id value
0 a 1
1 b 2
2 c 3
3 d 4
4 e 90
In [66]: df_sample.sample(3, replace = True, weights = df_sample.value)
Out[66]:
id value
4 e 90
4 e 90
4 e 90
二擂涛、多級(jí)索引
1. 多級(jí)索引及其表的結(jié)構(gòu)
為了更加清晰地說明具有多級(jí)索引的 DataFrame
結(jié)構(gòu),下面新構(gòu)造一張表聊记,讀者可以忽略這里的構(gòu)造方法歼指,它們將會(huì)在第4小節(jié)被更詳細(xì)地講解。
In [67]: np.random.seed(0)
In [68]: multi_index = pd.MultiIndex.from_product([list('ABCD'),
....: df.Gender.unique()], names=('School', 'Gender')) #行索引名稱
....:
In [69]: multi_column = pd.MultiIndex.from_product([['Height', 'Weight'],
....: df.Grade.unique()], names=('Indicator', 'Grade')) #列索引名稱
....:
In [70]: df_multi = pd.DataFrame(np.c_[(np.random.randn(8,4)*5 + 163).tolist(),
....: (np.random.randn(8,4)*5 + 65).tolist()],
....: index = multi_index,
....: columns = multi_column).round(1)
....:
In [71]: df_multi
Out[71]:
Indicator Height Weight
Grade Freshman Senior Sophomore Junior Freshman Senior Sophomore Junior
School Gender
A Female 171.8 165.0 167.9 174.2 60.6 55.1 63.3 65.8
Male 172.3 158.1 167.8 162.2 71.2 71.0 63.1 63.5
B Female 162.5 165.1 163.7 170.3 59.8 57.9 56.5 74.8
Male 166.8 163.6 165.2 164.7 62.5 62.8 58.7 68.9
C Female 170.5 162.0 164.6 158.7 56.9 63.9 60.5 66.9
Male 150.2 166.3 167.3 159.3 62.4 59.1 64.9 67.1
D Female 174.3 155.7 163.2 162.1 65.3 66.5 61.8 63.2
Male 170.7 170.3 163.8 164.9 61.6 63.2 60.9 56.4
下圖通過顏色區(qū)分甥雕,標(biāo)記了 DataFrame
的結(jié)構(gòu)踩身。與單層索引的表一樣,具備元素值社露、行索引和列索引三個(gè)部分挟阻。其中,這里的行索引和列索引都是 MultiIndex
類型峭弟,只不過 索引中的一個(gè)元素是元組 而不是單層索引中的標(biāo)量附鸽。例如,行索引的第四個(gè)元素為 ("B", "Male")
瞒瘸,列索引的第二個(gè)元素為 ("Height", "Senior")
坷备,這里需要注意,外層連續(xù)出現(xiàn)相同的值時(shí)情臭,第一次之后出現(xiàn)的會(huì)被隱藏顯示省撑,使結(jié)果的可讀性增強(qiáng)。
與單層索引類似俯在, MultiIndex
也具有名字屬性竟秫,圖中的 School
和 Gender
分別對(duì)應(yīng)了表的第一層和第二層行索引的名字, Indicator
和 Grade
分別對(duì)應(yīng)了第一層和第二層列索引的名字跷乐。
索引的名字和值屬性分別可以通過 names
和 values
獲得:
In [72]: df_multi.index.names #行索引的名稱
Out[72]: FrozenList(['School', 'Gender'])
In [73]: df_multi.columns.names #列索引的名稱
Out[73]: FrozenList(['Indicator', 'Grade'])
In [74]: df_multi.index.values #行索引的組合值
Out[74]:
array([('A', 'Female'), ('A', 'Male'), ('B', 'Female'), ('B', 'Male'),
('C', 'Female'), ('C', 'Male'), ('D', 'Female'), ('D', 'Male')],
dtype=object)
In [75]: df_multi.columns.values #列索引的組合值
Out[75]:
array([('Height', 'Freshman'), ('Height', 'Senior'),
('Height', 'Sophomore'), ('Height', 'Junior'),
('Weight', 'Freshman'), ('Weight', 'Senior'),
('Weight', 'Sophomore'), ('Weight', 'Junior')], dtype=object)
如果想要得到某一層的索引肥败,則需要通過 get_level_values
獲得:
In [76]: df_multi.index.get_level_values(0)
Out[76]: Index(['A', 'A', 'B', 'B', 'C', 'C', 'D', 'D'], dtype='object', name='School')
df_multi.index.get_level_values(1)
'''
Index(['Female', 'Male', 'Female', 'Male', 'Female', 'Male', 'Female', 'Male'], dtype='object', name='Gender')
'''
但對(duì)于索引而言,無論是單層還是多層,用戶都無法通過 index_obj[0] = item
的方式來修改元素馒稍,也不能通過 index_name[0] = new_name
的方式來修改名字皿哨,關(guān)于如何修改這些屬性的話題將在第三節(jié)被討論。
2. 多級(jí)索引中的loc索引器
熟悉了結(jié)構(gòu)后纽谒,現(xiàn)在回到原表往史,將學(xué)校和年級(jí)設(shè)為索引,此時(shí)的行是多級(jí)索引佛舱,列為單級(jí)索引椎例,由于默認(rèn)狀態(tài)的列索引不含名字,因此對(duì)應(yīng)于剛剛圖中 Indicator
和 Grade
的索引名位置是空缺的请祖。
In [77]: df_multi = df.set_index(['School', 'Grade'])
In [78]: df_multi.head()
Out[78]:
Name Gender Weight Transfer
School Grade
Shanghai Jiao Tong University Freshman Gaopeng Yang Female 46.0 N
Peking University Freshman Changqiang You Male 70.0 N
Shanghai Jiao Tong University Senior Mei Sun Male 89.0 N
Fudan University Sophomore Xiaojuan Sun Female 41.0 N
Sophomore Gaojuan You Male 74.0 N
由于多級(jí)索引中的單個(gè)元素以元組為單位订歪,因此之前在第一節(jié)介紹的 loc
和 iloc
方法完全可以照搬,只需把標(biāo)量的位置替換成對(duì)應(yīng)的元組肆捕,不過在索引前最好對(duì) MultiIndex
進(jìn)行排序以避免性能警告:
In [79]: df_multi = df_multi.sort_index()
In [80]: df_multi.loc[('Fudan University', 'Junior')].head() #把標(biāo)量的位置替換成對(duì)應(yīng)的元組
Out[80]:
Name Gender Weight Transfer
School Grade
Fudan University Junior Yanli You Female 48.0 N
Junior Chunqiang Chu Male 72.0 N
Junior Changfeng Lv Male 76.0 N
Junior Yanjuan Lv Female 49.0 NaN
Junior Gaoqiang Zhou Female 43.0 N
In [81]: df_multi.loc[[('Fudan University', 'Senior'),
....: ('Shanghai Jiao Tong University', 'Freshman')]].head() #兩個(gè)元組
....:
Out[81]:
Name Gender Weight Transfer
School Grade
Fudan University Senior Chengpeng Zheng Female 38.0 N
Senior Feng Zhou Female 47.0 N
Senior Gaomei Lv Female 34.0 N
Senior Chunli Lv Female 56.0 N
Senior Chengpeng Zhou Male 81.0 N
In [82]: df_multi.loc[df_multi.Weight > 70].head() # 布爾列表也是可用的
Out[82]:
Name Gender Weight Transfer
School Grade
Fudan University Freshman Feng Wang Male 74.0 N
Junior Chunqiang Chu Male 72.0 N
Junior Changfeng Lv Male 76.0 N
Senior Chengpeng Zhou Male 81.0 N
Senior Chengpeng Qian Male 73.0 Y
In [83]: df_multi.loc[lambda x:('Fudan University','Junior')].head()
Out[83]:
Name Gender Weight Transfer
School Grade
Fudan University Junior Yanli You Female 48.0 N
Junior Chunqiang Chu Male 72.0 N
Junior Changfeng Lv Male 76.0 N
Junior Yanjuan Lv Female 49.0 NaN
Junior Gaoqiang Zhou Female 43.0 N
練一練
與單層索引類似刷晋,若存在重復(fù)元素,則不能使用切片慎陵,請(qǐng)去除重復(fù)索引后給出一個(gè)元素切片的例子眼虱。
df_multi = df.drop_duplicates(['School','Grade']).set_index(['School', 'Grade']).sort_index() #必須先排序才能用切片
df_multi.loc[('Fudan University', 'Freshman'):('Peking University', 'Senior')]
此外,在多級(jí)索引中的元組有一種特殊的用法席纽,可以對(duì)多層的元素進(jìn)行交叉組合后索引捏悬,但同時(shí)需要指定 loc
的列,全選則用 :
表示润梯。其中过牙,每一層需要選中的元素用列表存放,傳入 loc
的形式為 [(level_0_list, level_1_list), cols]
纺铭。例如寇钉,想要得到所有北大和復(fù)旦的大二大三學(xué)生,可以如下寫出:
In [84]: res = df_multi.loc[(['Peking University', 'Fudan University'],
....: ['Sophomore', 'Junior']), :] #不同索引排列組合舶赔,即相當(dāng)于4個(gè)索引類型
....:
In [85]: res.head()
Out[85]:
Name Gender Weight Transfer
School Grade
Peking University Sophomore Changmei Xu Female 43.0 N
Sophomore Xiaopeng Qin Male NaN N
Sophomore Mei Xu Female 39.0 N
Sophomore Xiaoli Zhou Female 55.0 N
Sophomore Peng Han Female 34.0 NaN
In [86]: res.shape
Out[86]: (33, 4)
下面的語句和上面類似扫倡,但仍然傳入的是元素(這里為元組)的列表,它們的意義是不同的竟纳,表示的是選出北大的大三學(xué)生和復(fù)旦的大二學(xué)生:
In [87]: res = df_multi.loc[[('Peking University', 'Junior'),
....: ('Fudan University', 'Sophomore')]] #只有2個(gè)索引類型
....:
In [88]: res.head()
Out[88]:
Name Gender Weight Transfer
School Grade
Peking University Junior Juan Xu Female NaN N
Junior Changjuan You Female 47.0 N
Junior Gaoli Xu Female 48.0 N
Junior Gaoquan Zhou Male 70.0 N
Junior Qiang You Female 56.0 N
In [89]: res.shape
Out[89]: (16, 4)
3. IndexSlice對(duì)象(對(duì)每層進(jìn)行切片)
前面介紹的方法撵溃,即使在索引不重復(fù)的時(shí)候,也只能對(duì)元組整體進(jìn)行切片蚁袭,而不能對(duì)每層進(jìn)行切片征懈,也不允許將切片和布爾列表混合使用,引入 IndexSlice
對(duì)象就能解決這個(gè)問題揩悄。 Slice
對(duì)象一共有兩種形式,第一種為 loc[idx[*,*]]
型鬼悠,第二種為 loc[idx[*,*],idx[*,*]]
型删性,下面將進(jìn)行介紹亏娜。為了方便演示,下面構(gòu)造一個(gè) 索引不重復(fù)的 DataFrame
:
In [90]: np.random.seed(0)
In [91]: L1,L2 = ['A','B','C'],['a','b','c']
In [92]: mul_index1 = pd.MultiIndex.from_product([L1,L2],names=('Upper', 'Lower'))
In [93]: L3,L4 = ['D','E','F'],['d','e','f']
In [94]: mul_index2 = pd.MultiIndex.from_product([L3,L4],names=('Big', 'Small'))
In [95]: df_ex = pd.DataFrame(np.random.randint(-9,10,(9,9)),
....: index=mul_index1,
....: columns=mul_index2)
....:
In [96]: df_ex
Out[96]:
Big D E F
Small d e f d e f d e f
Upper Lower
A a 3 6 -9 -6 -6 -2 0 9 -5
b -3 3 -8 -3 -2 5 8 -4 4
c -1 0 7 -4 6 6 -9 9 -6
B a 8 5 -2 -9 -8 0 -9 1 -6
b 2 9 -7 -9 -9 -5 -4 -3 -1
c 8 6 -5 0 1 -8 -8 -2 0
C a -6 -3 2 5 9 -9 5 -6 3
b 1 2 -5 -3 -5 6 -6 3 -5
c -1 5 6 -6 6 4 7 8 -4
為了使用 silce
對(duì)象蹬挺,先要進(jìn)行定義:
In [97]: idx = pd.IndexSlice
【a】 loc[idx[*,*]]
型
這種情況并不能進(jìn)行多層分別切片维贺,前一個(gè) *
表示行的選擇,后一個(gè) *
表示列的選擇巴帮,與單純的 loc
是類似的:
In [98]: df_ex.loc[idx['C':, ('D', 'f'):]]
Out[98]:
Big D E F
Small f d e f d e f
Upper Lower
C a 2 5 9 -9 5 -6 3
b -5 -3 -5 6 -6 3 -5
c 6 -6 6 4 7 8 -4
另外溯泣,也支持布爾序列的索引:
In [99]: df_ex.loc[idx[:'A', lambda x:x.sum()>0]] # 列和大于0
Out[99]:
Big D F
Small d e e
Upper Lower
A a 3 6 9
b -3 3 -4
c -1 0 9
【b】 loc[idx[*,*],idx[*,*]]
型
這種情況能夠分層進(jìn)行切片,前一個(gè) idx
指代的是行索引榕茧,后一個(gè)是列索引垃沦。
In [100]: df_ex.loc[idx[:'A', 'b':], idx['E':, 'e':]]
Out[100]:
Big E F
Small e f e f
Upper Lower
A b -2 5 -4 4
c 6 6 9 -6
4. 多級(jí)索引的構(gòu)造
前面提到了多級(jí)索引表的結(jié)構(gòu)和切片,那么除了使用 set_index
之外用押,如何自己構(gòu)造多級(jí)索引呢肢簿?常用的有 from_tuples, from_arrays, from_product
三種方法,它們都是 pd.MultiIndex
對(duì)象下的函數(shù)蜻拨。
from_tuples
指根據(jù)傳入由元組組成的列表進(jìn)行構(gòu)造:
In [101]: my_tuple = [('a','cat'),('a','dog'),('b','cat'),('b','dog')]
In [102]: pd.MultiIndex.from_tuples(my_tuple, names=['First','Second'])
Out[102]:
MultiIndex([('a', 'cat'),
('a', 'dog'),
('b', 'cat'),
('b', 'dog')],
names=['First', 'Second'])
from_arrays
指根據(jù)傳入列表中池充,對(duì)應(yīng)層的列表進(jìn)行構(gòu)造:
In [103]: my_array = [list('aabb'), ['cat', 'dog']*2] #數(shù)量要對(duì)應(yīng),前面是4個(gè)元素缎讼,后面也要是4個(gè)元素
In [104]: pd.MultiIndex.from_arrays(my_array, names=['First','Second'])
Out[104]:
MultiIndex([('a', 'cat'),
('a', 'dog'),
('b', 'cat'),
('b', 'dog')],
names=['First', 'Second'])
from_product
指根據(jù)給定多個(gè)列表的笛卡爾積進(jìn)行構(gòu)造:
In [105]: my_list1 = ['a','b']
In [106]: my_list2 = ['cat','dog']
In [107]: pd.MultiIndex.from_product([my_list1,
.....: my_list2],
.....: names=['First','Second'])
.....:
Out[107]:
MultiIndex([('a', 'cat'),
('a', 'dog'),
('b', 'cat'),
('b', 'dog')],
names=['First', 'Second'])
三收夸、索引的常用方法
1. 索引層的交換和刪除
為了方便理解交換的過程登渣,這里構(gòu)造一個(gè)三級(jí)索引的例子:
In [108]: np.random.seed(0)
In [109]: L1,L2,L3 = ['A','B'],['a','b'],['alpha','beta']
In [110]: mul_index1 = pd.MultiIndex.from_product([L1,L2,L3],
.....: names=('Upper', 'Lower','Extra'))
.....:
In [111]: L4,L5,L6 = ['C','D'],['c','d'],['cat','dog']
In [112]: mul_index2 = pd.MultiIndex.from_product([L4,L5,L6],
.....: names=('Big', 'Small', 'Other'))
.....:
In [113]: df_ex = pd.DataFrame(np.random.randint(-9,10,(8,8)),
.....: index=mul_index1,
.....: columns=mul_index2)
.....:
In [114]: df_ex
Out[114]:
Big C D
Small c d c d
Other cat dog cat dog cat dog cat dog
Upper Lower Extra
A a alpha 3 6 -9 -6 -6 -2 0 9
beta -5 -3 3 -8 -3 -2 5 8
b alpha -4 4 -1 0 7 -4 6 6
beta -9 9 -6 8 5 -2 -9 -8
B a alpha 0 -9 1 -6 2 9 -7 -9
beta -9 -5 -4 -3 -1 8 6 -5
b alpha 0 1 -8 -8 -2 0 -6 -3
beta 2 5 9 -9 5 -6 3 1
交換swaplevel, reorder_levels
索引層的交換由 swaplevel
和 reorder_levels
完成貌矿,前者只能交換兩個(gè)層畔塔,而后者可以交換任意層摄职,兩者都可以指定交換的是軸是哪一個(gè)形真,即行索引或列索引:
In [115]: df_ex.swaplevel(0,2,axis=1).head() # 列索引的第一層和第三層交換
Out[115]:
Other cat dog cat dog cat dog cat dog
Small c c d d c c d d
Big C C C C D D D D
Upper Lower Extra
A a alpha 3 6 -9 -6 -6 -2 0 9
beta -5 -3 3 -8 -3 -2 5 8
b alpha -4 4 -1 0 7 -4 6 6
beta -9 9 -6 8 5 -2 -9 -8
B a alpha 0 -9 1 -6 2 9 -7 -9
In [116]: df_ex.reorder_levels([2,0,1],axis=0).head() # 列表數(shù)字指代原來索引中的層
Out[116]:
Big C D
Small c d c d
Other cat dog cat dog cat dog cat dog
Extra Upper Lower
alpha A a 3 6 -9 -6 -6 -2 0 9
beta A a -5 -3 3 -8 -3 -2 5 8
alpha A b -4 4 -1 0 7 -4 6 6
beta A b -9 9 -6 8 5 -2 -9 -8
alpha B a 0 -9 1 -6 2 9 -7 -9
軸之間的索引交換
這里只涉及行或列索引內(nèi)部的交換痊末,不同方向索引之間的交換將在第五章中被討論振惰。
刪除droplevel
若想要?jiǎng)h除某一層的索引这弧,可以使用 droplevel
方法:
In [117]: df_ex.droplevel(1,axis=1)
Out[117]:
Big C D
Other cat dog cat dog cat dog cat dog
Upper Lower Extra
A a alpha 3 6 -9 -6 -6 -2 0 9
beta -5 -3 3 -8 -3 -2 5 8
b alpha -4 4 -1 0 7 -4 6 6
beta -9 9 -6 8 5 -2 -9 -8
B a alpha 0 -9 1 -6 2 9 -7 -9
beta -9 -5 -4 -3 -1 8 6 -5
b alpha 0 1 -8 -8 -2 0 -6 -3
beta 2 5 9 -9 5 -6 3 1
In [118]: df_ex.droplevel([0,1],axis=0)
Out[118]:
Big C D
Small c d c d
Other cat dog cat dog cat dog cat dog
Extra
alpha 3 6 -9 -6 -6 -2 0 9
beta -5 -3 3 -8 -3 -2 5 8
alpha -4 4 -1 0 7 -4 6 6
beta -9 9 -6 8 5 -2 -9 -8
alpha 0 -9 1 -6 2 9 -7 -9
beta -9 -5 -4 -3 -1 8 6 -5
alpha 0 1 -8 -8 -2 0 -6 -3
beta 2 5 9 -9 5 -6 3 1
2. 索引屬性的修改rename_axis, rename
通過 rename_axis
可以對(duì)索引層的名字進(jìn)行修改捷凄,常用的修改方式是傳入字典的映射:
In [119]: df_ex.rename_axis(index={'Upper':'Changed_row'},
.....: columns={'Other':'Changed_Col'}).head()
.....:
Out[119]:
Big C D
Small c d c d
Changed_Col cat dog cat dog cat dog cat dog
Changed_row Lower Extra
A a alpha 3 6 -9 -6 -6 -2 0 9
beta -5 -3 3 -8 -3 -2 5 8
b alpha -4 4 -1 0 7 -4 6 6
beta -9 9 -6 8 5 -2 -9 -8
B a alpha 0 -9 1 -6 2 9 -7 -9
通過 rename
可以對(duì)索引的值進(jìn)行修改忱详,如果是多級(jí)索引需要指定修改的層號(hào) level
:
In [120]: df_ex.rename(columns={'cat':'not_cat'},
.....: level=2).head()
.....:
Out[120]:
Big C D
Small c d c d
Other not_cat dog not_cat dog not_cat dog not_cat dog
Upper Lower Extra
A a alpha 3 6 -9 -6 -6 -2 0 9
beta -5 -3 3 -8 -3 -2 5 8
b alpha -4 4 -1 0 7 -4 6 6
beta -9 9 -6 8 5 -2 -9 -8
B a alpha 0 -9 1 -6 2 9 -7 -9
傳入?yún)?shù)也可以是函數(shù),其輸入值就是索引元素:
In [121]: df_ex.rename(index=lambda x:str.upper(x),
.....: level=2).head() #level=2是指Extra那列的索引
.....:
Out[121]:
Big C D
Small c d c d
Other cat dog cat dog cat dog cat dog
Upper Lower Extra
A a ALPHA 3 6 -9 -6 -6 -2 0 9
BETA -5 -3 3 -8 -3 -2 5 8
b ALPHA -4 4 -1 0 7 -4 6 6
BETA -9 9 -6 8 5 -2 -9 -8
B a ALPHA 0 -9 1 -6 2 9 -7 -9
練一練
嘗試在
rename_axis
中使用函數(shù)完成與例子中一樣的功能跺涤。
df_ex.rename_axis(index=lambda x:x.replace('Upper',"changed_row"), columns=lambda x:x.replace('Other', 'Changed_Col')).head()
或者:
df_ex.rename_axis(index=lambda x: "changed_row" if x == 'Upper' else x, columns=lambda x:x.replace('Other', 'Changed_Col')).head() #if后面一定要有else匈睁,不然lambda不知道要返回什么值
對(duì)于整個(gè)索引的元素替換,可以利用迭代器實(shí)現(xiàn):
In [122]: new_values = iter(list('abcdefgh'))
In [123]: df_ex.rename(index=lambda x:next(new_values),
.....: level=2) #這里替換的是原來的數(shù)列里的alpha桶错,beta等
.....:
Out[123]:
Big C D
Small c d c d
Other cat dog cat dog cat dog cat dog
Upper Lower Extra
A a a 3 6 -9 -6 -6 -2 0 9
b -5 -3 3 -8 -3 -2 5 8
b c -4 4 -1 0 7 -4 6 6
d -9 9 -6 8 5 -2 -9 -8
B a e 0 -9 1 -6 2 9 -7 -9
f -9 -5 -4 -3 -1 8 6 -5
b g 0 1 -8 -8 -2 0 -6 -3
h 2 5 9 -9 5 -6 3 1
若想要對(duì)某個(gè)位置的元素進(jìn)行修改航唆,在單層索引時(shí)容易實(shí)現(xiàn),即先取出索引的 values
屬性院刁,再給對(duì)得到的列表進(jìn)行修改糯钙,最后再對(duì) index
對(duì)象重新賦值。但是如果是多級(jí)索引的話就有些麻煩,一個(gè)解決的方案是先把某一層索引臨時(shí)轉(zhuǎn)為表的元素任岸,然后再進(jìn)行修改再榄,最后重新設(shè)定為索引,下面一節(jié)將介紹這些操作享潜。
map方法的應(yīng)用
另外一個(gè)需要介紹的函數(shù)是 map
困鸥,它是定義在 Index
上的方法,與前面 rename
方法中層的函數(shù)式用法是類似的剑按,只不過它傳入的不是層的標(biāo)量值疾就,而是直接傳入索引的元組,這為用戶進(jìn)行跨層的修改提供了遍歷艺蝴。例如猬腰,可以等價(jià)地寫出上面的字符串轉(zhuǎn)大寫的操作:
In [124]: df_temp = df_ex.copy()
In [125]: new_idx = df_temp.index.map(lambda x: (x[0],
.....: x[1],
.....: str.upper(x[2])))
.....:
In [126]: df_temp.index = new_idx
In [127]: df_temp.head()
Out[127]:
Big C D
Small c d c d
Other cat dog cat dog cat dog cat dog
Upper Lower Extra
A a ALPHA 3 6 -9 -6 -6 -2 0 9
BETA -5 -3 3 -8 -3 -2 5 8
b ALPHA -4 4 -1 0 7 -4 6 6
BETA -9 9 -6 8 5 -2 -9 -8
B a ALPHA 0 -9 1 -6 2 9 -7 -9
關(guān)于 map
的另一個(gè)使用方法是對(duì)多級(jí)索引的壓縮,這在第四章和第五章的一些操作中是有用的:
In [128]: df_temp = df_ex.copy()
In [129]: new_idx = df_temp.index.map(lambda x: (x[0]+'-'+
.....: x[1]+'-'+
.....: x[2]))
.....:
In [130]: df_temp.index = new_idx
In [131]: df_temp.head() # 單層索引
Out[131]:
Big C D
Small c d c d
Other cat dog cat dog cat dog cat dog
A-a-alpha 3 6 -9 -6 -6 -2 0 9
A-a-beta -5 -3 3 -8 -3 -2 5 8
A-b-alpha -4 4 -1 0 7 -4 6 6
A-b-beta -9 9 -6 8 5 -2 -9 -8
B-a-alpha 0 -9 1 -6 2 9 -7 -9
同時(shí)吴趴,也可以反向地展開:
In [132]: new_idx = df_temp.index.map(lambda x:tuple(x.split('-')))
In [133]: df_temp.index = new_idx
In [134]: df_temp.head() # 三層索引
Out[134]:
Big C D
Small c d c d
Other cat dog cat dog cat dog cat dog
A a alpha 3 6 -9 -6 -6 -2 0 9
beta -5 -3 3 -8 -3 -2 5 8
b alpha -4 4 -1 0 7 -4 6 6
beta -9 9 -6 8 5 -2 -9 -8
B a alpha 0 -9 1 -6 2 9 -7 -9
3. 索引的設(shè)置與重置set_index, reset_index
為了說明本節(jié)的函數(shù)漆诽,下面構(gòu)造一個(gè)新表:
In [135]: df_new = pd.DataFrame({'A':list('aacd'),
.....: 'B':list('PQRT'),
.....: 'C':[1,2,3,4]})
.....:
In [136]: df_new
Out[136]:
A B C
0 a P 1
1 a Q 2
2 c R 3
3 d T 4
索引的設(shè)置可以使用 set_index
完成,這里的主要參數(shù)是 append
锣枝,表示是否來保留原來的索引厢拭,直接把新設(shè)定的添加到原索引的內(nèi)層:
In [137]: df_new.set_index('A')
Out[137]:
B C
A
a P 1
a Q 2
c R 3
d T 4
In [138]: df_new.set_index('A', append=True) #保留原來的索引
Out[138]:
B C
A
0 a P 1
1 a Q 2
2 c R 3
3 d T 4
可以同時(shí)指定多個(gè)列作為索引:
In [139]: df_new.set_index(['A', 'B'])
Out[139]:
C
A B
a P 1
Q 2
c R 3
d T 4
如果想要添加索引的列沒有出現(xiàn)再其中,那么可以直接在參數(shù)中傳入相應(yīng)的 Series
:
In [140]: my_index = pd.Series(list('WXYZ'), name='D')
In [141]: df_new = df_new.set_index(['A', my_index])
In [142]: df_new
Out[142]:
B C
A D
a W P 1
X Q 2
c Y R 3
d Z T 4
reset_index
是 set_index
的逆函數(shù)撇叁,其主要參數(shù)是 drop
供鸠,表示是否要把去掉的索引層丟棄,而不是添加到列中:
In [143]: df_new.reset_index(['D'])
Out[143]:
D B C
A
a W P 1
a X Q 2
c Y R 3
d Z T 4
In [144]: df_new.reset_index(['D'], drop=True)
Out[144]:
B C
A
a P 1
a Q 2
c R 3
d T 4
如果重置了所有的索引陨闹,那么 pandas
會(huì)直接重新生成一個(gè)默認(rèn)索引:
In [145]: df_new.reset_index()
Out[145]:
A D B C
0 a W P 1
1 a X Q 2
2 c Y R 3
3 d Z T 4
4. 索引的變形reindex,reindex_like
在某些場(chǎng)合下楞捂,需要對(duì)索引做一些擴(kuò)充或者剔除,更具體地要求是給定一個(gè)新的索引趋厉,把原表中相應(yīng)的索引對(duì)應(yīng)元素填充到新索引構(gòu)成的表中寨闹。例如,下面的表中給出了員工信息君账,需要重新制作一張新的表繁堡,要求增加一名員工的同時(shí)去掉身高列并增加性別列:
In [146]: df_reindex = pd.DataFrame({"Weight":[60,70,80],
.....: "Height":[176,180,179]},
.....: index=['1001','1003','1002'])
.....:
In [147]: df_reindex
Out[147]:
Weight Height
1001 60 176
1003 70 180
1002 80 179
In [148]: df_reindex.reindex(index=['1001','1002','1003','1004'],
.....: columns=['Weight','Gender'])
.....:
Out[148]:
Weight Gender
1001 60.0 NaN
1002 80.0 NaN
1003 70.0 NaN
1004 NaN NaN
這種需求常出現(xiàn)在時(shí)間序列索引的時(shí)間點(diǎn)填充以及 ID
編號(hào)的擴(kuò)充。另外乡数,需要注意的是原來表中的數(shù)據(jù)和新表中會(huì)根據(jù)索引自動(dòng)對(duì)其椭蹄,例如原先的1002號(hào)位置在1003號(hào)之后,而新表中相反净赴,那么 reindex
中會(huì)根據(jù)元素對(duì)其绳矩,與位置無關(guān)。
還有一個(gè)與 reindex
功能類似的函數(shù)是 reindex_like
玖翅,其功能是仿照傳入的表的索引來進(jìn)行被調(diào)用表索引的變形翼馆。例如割以,現(xiàn)在以及存在一張表具備了目標(biāo)索引的條件,那么上述功能可以如下等價(jià)地寫出:
In [149]: df_existed = pd.DataFrame(index=['1001','1002','1003','1004'],
.....: columns=['Weight','Gender'])
.....:
In [150]: df_reindex.reindex_like(df_existed)
Out[150]:
Weight Gender
1001 60.0 NaN
1002 80.0 NaN
1003 70.0 NaN
1004 NaN NaN
四写妥、索引運(yùn)算
1. 集合的運(yùn)算法則
經(jīng)常會(huì)有一種利用集合運(yùn)算來取出符合條件行的需求拳球,例如有兩張表 A
和 B
审姓,它們的索引都是員工編號(hào)珍特,現(xiàn)在需要篩選出兩表索引交集的所有員工信息,此時(shí)通過 Index
上的運(yùn)算操作就很容易實(shí)現(xiàn)魔吐。
不過在此之前扎筒,不妨先復(fù)習(xí)一下常見的四種集合運(yùn)算:
2. 一般的索引運(yùn)算
由于集合的元素是互異的,但是索引中可能有相同的元素酬姆,先用 unique
去重后再進(jìn)行運(yùn)算嗜桌。下面構(gòu)造兩張最為簡(jiǎn)單的示例表進(jìn)行演示:
In [151]: df_set_1 = pd.DataFrame([[0,1],[1,2],[3,4]],
.....: index = pd.Index(['a','b','a'],name='id1'))
.....:
In [152]: df_set_2 = pd.DataFrame([[4,5],[2,6],[7,1]],
.....: index = pd.Index(['b','b','c'],name='id2'))
.....:
In [153]: id1, id2 = df_set_1.index.unique(), df_set_2.index.unique()
In [154]: id1.intersection(id2)
Out[154]: Index(['b'], dtype='object')
In [155]: id1.union(id2)
Out[155]: Index(['a', 'b', 'c'], dtype='object')
In [156]: id1.difference(id2)
Out[156]: Index(['a'], dtype='object')
In [157]: id1.symmetric_difference(id2)
Out[157]: Index(['a', 'c'], dtype='object')
上述的四類運(yùn)算還可以用等價(jià)的符號(hào)表示代替如下:
In [158]: id1 & id2
Out[158]: Index(['b'], dtype='object')
In [159]: id1 | id2
Out[159]: Index(['a', 'b', 'c'], dtype='object')
In [160]: (id1 ^ id2) & id1
Out[160]: Index(['a'], dtype='object')
In [161]: id1 ^ id2 # ^符號(hào)即對(duì)稱差
Out[161]: Index(['a', 'c'], dtype='object')
若兩張表需要做集合運(yùn)算的列 并沒有被設(shè)置索引,一種辦法是先轉(zhuǎn)成索引辞色,運(yùn)算后再恢復(fù)骨宠,另一種方法是利用 isin
函數(shù),例如在重置索引的第一張表中選出id列交集的所在行:
In [162]: df_set_in_col_1 = df_set_1.reset_index()
In [163]: df_set_in_col_2 = df_set_2.reset_index()
In [164]: df_set_in_col_1
Out[164]:
id1 0 1
0 a 0 1
1 b 1 2
2 a 3 4
In [165]: df_set_in_col_2
Out[165]:
id2 0 1
0 b 4 5
1 b 2 6
2 c 7 1
In [166]: df_set_in_col_1[df_set_in_col_1.id1.isin(df_set_in_col_2.id2)]
Out[166]:
id1 0 1
1 b 1 2
五相满、練習(xí)
Ex1:公司員工數(shù)據(jù)集
現(xiàn)有一份公司員工數(shù)據(jù)集:
In [167]: df = pd.read_csv('data/company.csv')
In [168]: df.head(3)
Out[168]:
EmployeeID birthdate_key age city_name department job_title gender
0 1318 1/3/1954 61 Vancouver Executive CEO M
1 1319 1/3/1957 58 Vancouver Executive VP Stores F
2 1320 1/2/1955 60 Vancouver Executive Legal Counsel F
- 分別只使用
query
和loc
選出年齡不超過四十歲且工作部門為Dairy
或Bakery
的男性层亿。
df.query('(age<=40)&((department=="Dairy")|(department=="Bakery"))&(gender=="M")')
#loc: 注意在[]中使用括號(hào)和query的效果是不一樣的,需要單獨(dú)寫條件并組合
condition_1_1 = df.department=="Dairy"
condition_1_2 = df.department=="Bakery"
condition_1 = condition_1_1 | condition_1_2
condition_2 = df.age<=40
condition_3 = df.gender == 'M'
df.loc[condition_1&condition_2&condition_3]
- 選出員工
ID
號(hào) 為奇數(shù)所在行的第1立美、第3和倒數(shù)第2列匿又。
df.loc[df.EmployeeID%2==1].iloc[:, [0,2,-2]]
答案:
df.iloc[(df.EmployeeID%2==1).values,[0,2,-2]].head()
- 按照以下步驟進(jìn)行索引操作:
- 把后三列設(shè)為索引后交換內(nèi)外兩層
- 恢復(fù)中間一層
- 修改外層索引名為
Gender
- 用下劃線合并兩層行索引
- 把行索引拆分為原狀態(tài)
- 修改索引名為原表名稱
- 恢復(fù)默認(rèn)索引并將列保持為原表的相對(duì)位置
# 把后三列設(shè)為索引后交換內(nèi)外兩層
df_ex = df.set_index(['department','job_title','gender'])
df_ex = df_ex.swaplevel(0,1,axis=0)
# 恢復(fù)中間一層(把中間一層從索引變回到column)
df_ex = df_ex.reset_index(level=1)
# 修改外層索引名為 `Gender`
df_ex =df_ex.rename_axis(index={'department':'gender'})
# 用下劃線合并兩層行索引
new_idx = df_ex.index.map(lambda x: (x[0]+'_'+x[1]))
df_ex.index = new_idx
# 把行索引拆分為原狀態(tài)
new_idx = df_ex.index.map(lambda x:tuple(x.split('_')))
df_ex.index = new_idx
# 修改索引名為原表名稱
df_ex = df_ex.rename_axis(index=['job_title','gender'])
# 恢復(fù)默認(rèn)索引并將列保持為原表的相對(duì)位置
df_ex = df_ex.reset_index().reindex(df.columns,axis=1) #要加axis=1,否則會(huì)默認(rèn)等于0
Ex2:巧克力數(shù)據(jù)集
現(xiàn)有一份關(guān)于巧克力評(píng)價(jià)的數(shù)據(jù)集:
In [169]: df = pd.read_csv('data/chocolate.csv')
In [170]: df.head(3)
Out[170]:
Company Review\nDate Cocoa\nPercent Company\nLocation Rating
0 A. Morin 2016 63% France 3.75
1 A. Morin 2015 70% France 2.75
2 A. Morin 2015 70% France 3.00
- 把列索引名中的
\n
替換為空格建蹄。
df.rename(columns=lambda x: x.replace('\n',' '))
- 巧克力
Rating
評(píng)分為1至5碌更,每0.25分一檔,請(qǐng)選出2.75分及以下且可可含量Cocoa Percent
高于中位數(shù)的樣本洞慎。
df['Cocoa Percent'] = df['Cocoa Percent'].apply(lambda x:float(x[:-1])/100)
df.query('(Rating<3)&(`Cocoa Percent`>`Cocoa Percent`.median())') #注意列名有空格的要用``符號(hào)包起來痛单;中位數(shù)是median不是mean
- 將
Review Date
和Company Location
設(shè)為索引后,選出Review Date
在2012年之后且Company Location
不屬于France, Canada, Amsterdam, Belgium
的樣本劲腿。
df.set_index(['Review Date',"Company Location"])
df.query('(`Review Date`>=2012) and (`Company Location` not in ["France", "Canada", "Amsterdam", "Belgium"])')
答案:
idx = pd.IndexSlice
exclude = ['France', 'Canada', 'Amsterdam', 'Belgium']
res = df.set_index(['Review Date', 'Company Location']).sort_index(level=0)
res.loc[idx[2012:,~res.index.get_level_values(1).isin(exclude)],:].head(3)