第三章 索引

來源: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é)合 DataFramedtypes 屬性在 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 innot 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%的樣本)销部。

replaceweights 分別是指是否放回和每個(gè)樣本的抽樣相對(duì)概率,當(dāng) replace = True則表示有放回抽樣制跟。例如舅桩,對(duì)下面構(gòu)造的 df_samplevalue 值的相對(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 也具有名字屬性竟秫,圖中的 SchoolGender 分別對(duì)應(yīng)了表的第一層和第二層行索引的名字, IndicatorGrade 分別對(duì)應(yīng)了第一層和第二層列索引的名字跷乐。

索引的名字和值屬性分別可以通過 namesvalues 獲得:

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)于剛剛圖中 IndicatorGrade 的索引名位置是空缺的请祖。

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é)介紹的 lociloc 方法完全可以照搬,只需把標(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

索引層的交換由 swaplevelreorder_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_indexset_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)算來取出符合條件行的需求拳球,例如有兩張表 AB 审姓,它們的索引都是員工編號(hào)珍特,現(xiàn)在需要篩選出兩表索引交集的所有員工信息,此時(shí)通過 Index上的運(yùn)算操作就很容易實(shí)現(xiàn)魔吐。

不過在此之前扎筒,不妨先復(fù)習(xí)一下常見的四種集合運(yùn)算:

\rm S_A.intersection(S_B) = \rm S_A \cap S_B \Leftrightarrow \rm \{x|x\in S_A\, and\, x\in S_B\}
\rm S_A.union(S_B) = \rm S_A \cup S_B \Leftrightarrow \rm \{x|x\in S_A\, or\, x\in S_B\}
\rm S_A.difference(S_B) = \rm S_A - S_B \Leftrightarrow \rm \{x|x\in S_A\, and\, x\notin S_B\}
\rm S_A.symmetric\_difference(S_B) = \rm S_A\triangle S_B\Leftrightarrow \rm \{x|x\in S_A\cup S_B - S_A\cap S_B\}

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
  1. 分別只使用 queryloc 選出年齡不超過四十歲且工作部門為 DairyBakery 的男性层亿。
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]
  1. 選出員工 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()
  1. 按照以下步驟進(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
  1. 把列索引名中的 \n 替換為空格建蹄。
df.rename(columns=lambda x: x.replace('\n',' '))
  1. 巧克力 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
  1. Review DateCompany 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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末旭绒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子谆棱,更是在濱河造成了極大的恐慌快压,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垃瞧,死亡現(xiàn)場(chǎng)離奇詭異蔫劣,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)个从,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門脉幢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來歪沃,“玉大人,你說我怎么就攤上這事嫌松』κ铮” “怎么了?”我有些...
    開封第一講書人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵萎羔,是天一觀的道長(zhǎng)液走。 經(jīng)常有香客問我,道長(zhǎng)贾陷,這世上最難降的妖魔是什么缘眶? 我笑而不...
    開封第一講書人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮髓废,結(jié)果婚禮上巷懈,老公的妹妹穿的比我還像新娘。我一直安慰自己慌洪,他們只是感情好顶燕,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著冈爹,像睡著了一般涌攻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上犯助,一...
    開封第一講書人閱讀 49,837評(píng)論 1 290
  • 那天癣漆,我揣著相機(jī)與錄音,去河邊找鬼剂买。 笑死惠爽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瞬哼。 我是一名探鬼主播婚肆,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼坐慰!你這毒婦竟也來了较性?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬榮一對(duì)情侶失蹤结胀,失蹤者是張志新(化名)和其女友劉穎赞咙,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糟港,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡攀操,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秸抚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片速和。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡歹垫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出颠放,到底是詐尸還是另有隱情排惨,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布碰凶,位于F島的核電站暮芭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏痒留。R本人自食惡果不足惜谴麦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一蠢沿、第九天 我趴在偏房一處隱蔽的房頂上張望伸头。 院中可真熱鬧,春花似錦舷蟀、人聲如沸恤磷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扫步。三九已至,卻和暖如春匈子,著一層夾襖步出監(jiān)牢的瞬間河胎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工虎敦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留游岳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓其徙,卻偏偏與公主長(zhǎng)得像胚迫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唾那,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容