DataWhale組隊學(xué)習(xí)之索引

參考datawhale:https://datawhalechina.github.io/joyful-pandas/build/html/%E7%9B%AE%E5%BD%95/ch3.html

import numpy as np

import pandas as pd

一、索引器

  1. 表的列索引

列索引是最常見的索引形式,一般通過 [] 來實現(xiàn)。通過 [列名] 可以從 DataFrame 中取出相應(yīng)的列,返回值為 Series ,例如從表中取出姓名一列:

df = pd.read_csv('data/learn_pandas.csv',
   ...:                  usecols = ['School', 'Grade', 'Name', 'Gender',
   ...:                             'Weight', 'Transfer'])
   ...: 
df['Name'].head()
 
0      Gaopeng Yang
1    Changqiang You
2           Mei Sun
3      Xiaojuan Sun
4       Gaojuan You
Name: Name, dtype: object

如果要取出多個列,則可以通過 [列名組成的列表] ,其返回值為一個 DataFrame ,例如從表中取出性別和姓名兩列:

df[['Gender', 'Name']].head()
   Gender            Name
0  Female    Gaopeng Yang
1    Male  Changqiang You
2    Male         Mei Sun
3  Female    Xiaojuan Sun
4    Male     Gaojuan You

此外,若要取出單列,且列名中不包含空格,則可以用 .列名 取出,這和 [列名] 是等價的:

df.Name.head()
0      Gaopeng Yang
1    Changqiang You
2           Mei Sun
3      Xiaojuan Sun
4       Gaojuan You
Name: Name, dtype: object
  1. 序列的行索引
    【a】以字符串為索引的 Series

如果取出單個索引的對應(yīng)元素,則可以使用 [item] ,若 Series 只有單個值對應(yīng),則返回這個標(biāo)量值,如果有多個值對應(yīng),則返回一個 Series:

 s = pd.Series([1, 2, 3, 4, 5, 6],
   ...:                index=['a', 'b', 'a', 'a', 'a', 'c'])
   ...: 

s['a']
 
a    1
a    3
a    4
a    5
dtype: int64

 s['b']
 2

如果取出多個索引的對應(yīng)元素,則可以使用 [items的列表] :

s[['c', 'b']]
c    6
b    2
dtype: int64

如果想要取出某兩個索引之間的元素,并且這兩個索引是在整個索引中唯一出現(xiàn),則可以使用切片,同時需要注意這里的切片會包含兩個端點(diǎn):

s['c': 'b': -2]

c    6
a    4
b    2
dtype: int64

【b】以整數(shù)為索引的 Series

在使用數(shù)據(jù)的讀入函數(shù)時,如果不特別指定所對應(yīng)的列作為索引,那么會生成從0開始的整數(shù)索引作為默認(rèn)索引。當(dāng)然,任意一組符合長度要求的整數(shù)都可以作為索引。

和字符串一樣,如果使用 [int] 或 [int_list] ,則可以取出對應(yīng)索引 元素 的值:

s = pd.Series(['a', 'b', 'c', 'd', 'e', 'f'],
   ....:               index=[1, 3, 1, 2, 5, 4])
   ....: 
s[1]
1    a
1    c
dtype: object
s[[2,3]]
2    d
3    b
dtype: object

如果使用整數(shù)切片,則會取出對應(yīng)索引 位置 的值,注意這里的整數(shù)切片同 Python 中的切片一樣不包含右端點(diǎn):

 s[1:-1:2]
3    b
2    d
dtype: object

  1. loc索引器
    前面講到了對 DataFrame 的列進(jìn)行選取,下面要討論其行的選取。對于表而言,有兩種索引器,一種是基于 元素 的 loc 索引器,另一種是基于 位置 的 iloc 索引器。

loc 索引器的一般形式是 loc[*, ] ,其中第一個 * 代表行的選擇,第二個 * 代表列的選擇,如果省略第二個位置寫作 loc[] ,這個 * 是指行的篩選。其中, * 的位置一共有五類合法對象,分別是:單個元素、元素列表、元素切片、布爾列表以及函數(shù),下面將依次說明。

為了演示相應(yīng)操作,先利用 set_index 方法把 Name 列設(shè)為索引,關(guān)于該函數(shù)的其他用法將在多級索引一章介紹。

 df_demo = df.set_index('Name')

 df_demo.head()
 
                                       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】 * 為單個元素

此時,直接取出相應(yīng)的行或列,如果該元素在索引中重復(fù)則結(jié)果為 DataFrame,否則為 Series :

 df_demo.loc['Qiang Sun'] # 多個人叫此名字

                                  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

 df_demo.loc['Quan Zhao'] # 名字唯一
 
School      Shanghai Jiao Tong University
Grade                              Junior
Gender                             Female
Weight                                 53
Transfer                                N
Name: Quan Zhao, dtype: object

也可以同時選擇行和列:

 df_demo.loc['Qiang Sun', 'School'] # 返回Series

Name
Qiang Sun              Tsinghua University
Qiang Sun              Tsinghua University
Qiang Sun    Shanghai Jiao Tong University
Name: School, dtype: object

 df_demo.loc['Quan Zhao', 'School'] # 返回單個元素
 'Shanghai Jiao Tong University'

【b】 * 為元素列表

此時,取出列表中所有元素值對應(yīng)的行或列:

 df_demo.loc[['Qiang Sun','Quan Zhao'], ['School','Gender']]

                                  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 使用字符串索引時提到,如果是唯一值的起點(diǎn)和終點(diǎn)字符,那么就可以使用切片,并且包含兩個端點(diǎn),如果不唯一則報錯:

 df_demo.loc['Gaojuan You':'Gaoqiang Qian', 'School':'Gender']

                                      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ù)切片的時候和上面字符串索引的要求一致,都是 元素 切片,包含端點(diǎn)且起點(diǎn)、終點(diǎn)不允許有重復(fù)值。

 df_loc_slice_demo = df_demo.copy()

 df_loc_slice_demo.index = range(df_demo.shape[0],0,-1)

 df_loc_slice_demo.loc[5:3]
 
                          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

 df_loc_slice_demo.loc[3:5] # 沒有返回,說明不是整數(shù)位置切片
 
Empty DataFrame
Columns: [School, Grade, Gender, Weight, Transfer]
Index: []

【d】 * 為布爾列表

在實際的數(shù)據(jù)處理中,根據(jù)條件來篩選行是極其常見的,此處傳入 loc 的布爾列表與 DataFrame 長度相同,且列表為 True 的位置所對應(yīng)的行會被選中, False 則會被剔除。

例如,選出體重超過70kg的學(xué)生:

 df_demo.loc[df_demo.Weight>70].head()
 
                                      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 方法返回的布爾列表等價寫出,例如選出所有大一和大四的同學(xué)信息:

 df_demo.loc[df_demo.Grade.isin(['Freshman', 'Senior'])].head()
 
                                       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

對于復(fù)合條件而言,可以用 |(或), &(且), ~(取反) 的組合來實現(xiàn),例如選出復(fù)旦大學(xué)中體重超過70kg的大四學(xué)生,或者北大男生中體重超過80kg的非大四的學(xué)生:

 condition_1_1 = df_demo.School == 'Fudan University'

 condition_1_2 = df_demo.Grade == 'Senior'

 condition_1_3 = df_demo.Weight > 70

 condition_1 = condition_1_1 & condition_1_2 & condition_1_3

 condition_2_1 = df_demo.School == 'Peking University'

condition_2_2 = df_demo.Grade == 'Senior'

 condition_2_3 = df_demo.Weight > 80

 condition_2 = condition_2_1 & (~condition_2_2) & condition_2_3

 df_demo.loc[condition_1 | condition_2]
 
                           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

【e】 * 為函數(shù)

這里的函數(shù),必須以前面的四種合法形式之一為返回值,并且函數(shù)的輸入值為 DataFrame 本身。假設(shè)仍然是上述復(fù)合條件篩選的例子,可以把邏輯寫入一個函數(shù)中再返回,需要注意的是函數(shù)的形式參數(shù) x 本質(zhì)上即為 df_demo :

 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
   ....: 

 df_demo.loc[condition]
 
                           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á)式,其返回值也同樣必須是先前提到的四種形式之一:

 df_demo.loc[lambda x:'Quan Zhao', lambda x:'Gender']
 'Female'

由于函數(shù)無法返回如 start: end: step 的切片形式,故返回切片時要用 slice 對象進(jìn)行包裝:

 df_demo.loc[lambda x: slice('Gaojuan You', 'Gaoqiang Qian')]
 
                                      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

最后需要指出的是,對于 Series 也可以使用 loc 索引,其遵循的原則與 DataFrame 中用于行篩選的 loc[*] 完全一致,此處不再贅述。
在對表或者序列賦值時,應(yīng)當(dāng)在使用一層索引器后直接進(jìn)行賦值操作,這樣做是由于進(jìn)行多次索引后賦值是賦在臨時返回的 copy 副本上的,而沒有真正修改元素從而報出 SettingWithCopyWarning 警告。例如,下面給出的例子:

 df_chain = pd.DataFrame([[0,0],[1,0],[-1,0]], columns=list('AB'))

 df_chain
 
   A  B
0  0  0
1  1  0
2 -1  0

 import warnings

 with warnings.catch_warnings():
   ....:     warnings.filterwarnings('error')
   ....:     try:
   ....:         df_chain[df_chain.A!=0].B = 1 # 使用方括號列索引后,再使用點(diǎn)的列索引
   ....:     except Warning as w:
   ....:         Warning_Msg = w
   ....: 

 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

 df_chain

   A  B
0  0  0
1  1  0
2 -1  0

 df_chain.loc[df_chain.A!=0,'B'] = 1

 df_chain
Out[50]: 
   A  B
0  0  0
1  1  1
2 -1  1
  1. iloc索引器
    iloc 的使用與 loc 完全類似,只不過是針對位置進(jìn)行篩選,在相應(yīng)的 * 位置處一共也有五類合法對象,分別是:整數(shù)、整數(shù)列表、整數(shù)切片、布爾列表以及函數(shù),函數(shù)的返回值必須是前面的四類合法對象中的一個,其輸入同樣也為 DataFrame 本身。
 df_demo.iloc[1, 1] # 第二行第二列
Out[51]: 'Freshman'

 df_demo.iloc[[0, 1], [0, 1]] # 前兩行前兩列

                                       School     Grade
Name                                                   
Gaopeng Yang    Shanghai Jiao Tong University  Freshman
Changqiang You              Peking University  Freshman

 df_demo.iloc[1: 4, 2:4] # 切片不包含結(jié)束端點(diǎn)
 
                Gender  Weight
Name                          
Changqiang You    Male    70.0
Mei Sun           Male    89.0
Xiaojuan Sun    Female    41.0

 df_demo.iloc[lambda x: slice(1, 4)] # 傳入切片為返回值的函數(shù)
 
                                       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

在使用布爾列表的時候要特別注意,不能傳入 Series 而必須傳入序列的 values ,否則會報錯。因此,在使用布爾篩選的時候還是應(yīng)當(dāng)優(yōu)先考慮 loc 的方式。

例如,選出體重超過80kg的學(xué)生:

 df_demo.iloc[(df_demo.Weight>80).values].head()
 
                                       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

對 Series 而言同樣也可以通過 iloc 返回相應(yīng)位置的值或子序列:

 df_demo.School.iloc[1]
 'Peking University'

 df_demo.School.iloc[1:5:2]
 
Name
Changqiang You    Peking University
Xiaojuan Sun       Fudan University
Name: School, dtype: object
  1. query方法
    在 pandas 中,支持把字符串形式的查詢表達(dá)式傳入 query 方法來查詢數(shù)據(jù),其表達(dá)式的執(zhí)行結(jié)果必須返回布爾列表。在進(jìn)行復(fù)雜索引時,由于這種檢索方式無需像普通方法一樣重復(fù)使用 DataFrame 的名字來引用列名,一般而言會使代碼長度在不降低可讀性的前提下有所減少。

例如,將 loc 一節(jié)中的復(fù)合條件查詢例子可以如下改寫:

 df.query('((School == "Fudan University")&'
   ....:          ' (Grade == "Senior")&'
   ....:          ' (Weight > 70))|'
   ....:          '((School == "Peking University")&'
   ....:          ' (Grade != "Senior")&'
   ....:          ' (Weight > 80))')
   ....: 

                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á)式中,幫用戶注冊了所有來自 DataFrame 的列名,所有屬于該 Series 的方法都可以被調(diào)用,和正常的函數(shù)調(diào)用并沒有區(qū)別,例如查詢體重超過均值的學(xué)生:

 df.query('Weight > Weight.mean()').head()

                           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 中還注冊了若干英語的字面用法,幫助提高可讀性,例如: or, and, or, is in, not in 。例如,篩選出男生中不是大一大二的學(xué)生:

df.query('(Grade not in ["Freshman", "Sophomore"]) and'
   ....:          '(Gender == "Male")').head()
   ....: 
 
                           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)與列表的比較時, == 和 != 分別表示元素出現(xiàn)在列表和沒有出現(xiàn)在列表,等價于 is in 和 not in,例如查詢所有大三和大四的學(xué)生:

 df.query('Grade == ["Junior", "Senior"]').head()

                           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

對于 query 中的字符串,如果要引用外部變量,只需在變量名前加 @ 符號。例如,取出體重位于70kg到80kg之間的學(xué)生:

 low, high =70, 80

 df.query('Weight.between(@low, @high)').head()
 
                           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
  1. 隨機(jī)抽樣
    如果把 DataFrame 的每一行看作一個樣本,或把每一列看作一個特征,再把整個 DataFrame 看作總體,想要對樣本或特征進(jìn)行隨機(jī)抽樣就可以用 sample 函數(shù)。有時在拿到大型數(shù)據(jù)集后,想要對統(tǒng)計特征進(jìn)行計算來了解數(shù)據(jù)的大致分布,但是這很費(fèi)時間。同時,由于許多統(tǒng)計特征在等概率不放回的簡單隨機(jī)抽樣條件下,是總體統(tǒng)計特征的無偏估計,比如樣本均值和總體均值,那么就可以先從整張表中抽出一部分來做近似估計。

sample 函數(shù)中的主要參數(shù)為 n, axis, frac, replace, weights ,前三個分別是指抽樣數(shù)量、抽樣的方向(0為行、1為列)和抽樣比例(0.3則為從總體中抽出30%的樣本)。

replace 和 weights 分別是指是否放回和每個樣本的抽樣相對概率,當(dāng) replace = True 則表示有放回抽樣。例如,對下面構(gòu)造的 df_sample 以 value 值的相對大小為抽樣概率進(jìn)行有放回抽樣,抽樣數(shù)量為3。

df_sample = pd.DataFrame({'id': list('abcde'),
   ....:                           'value': [1, 2, 3, 4, 90]})
   ....: 

 df_sample
 
  id  value
0  a      1
1  b      2
2  c      3
3  d      4
4  e     90

 df_sample.sample(3, replace = True, weights = df_sample.value)
 
  id  value
4  e     90
4  e     90
4  e     90

二、多級索引

  1. 多級索引及其表的結(jié)構(gòu)
    為了更加清晰地說明具有多級索引的 DataFrame 結(jié)構(gòu),下面新構(gòu)造一張表,讀者可以忽略這里的構(gòu)造方法,它們將會在第4小節(jié)被更詳細(xì)地講解。
 np.random.seed(0)

 multi_index = pd.MultiIndex.from_product([list('ABCD'),
   ....:               df.Gender.unique()], names=('School', 'Gender'))
   ....: 

 multi_column = pd.MultiIndex.from_product([['Height', 'Weight'],
   ....:                df.Grade.unique()], names=('Indicator', 'Grade'))
   ....: 

 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)
   ....: 

 df_multi
 
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)。與單層索引的表一樣,具備元素值、行索引和列索引三個部分。其中,這里的行索引和列索引都是 MultiIndex 類型,只不過 索引中的一個元素是元組 而不是單層索引中的標(biāo)量。例如,行索引的第四個元素為 ("B", "Male") ,列索引的第二個元素為 ("Height", "Senior") ,這里需要注意,外層連續(xù)出現(xiàn)相同的值時,第一次之后出現(xiàn)的會被隱藏顯示,使結(jié)果的可讀性增強(qiáng)。

E574C4~1.PNG

與單層索引類似, MultiIndex 也具有名字屬性,圖中的 School 和 Gender 分別對應(yīng)了表的第一層和第二層行索引的名字, Indicator 和 Grade 分別對應(yīng)了第一層和第二層列索引的名字。

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

 df_multi.index.names
 FrozenList(['School', 'Gender'])

 df_multi.columns.names
 FrozenList(['Indicator', 'Grade'])

 df_multi.index.values
 
array([('A', 'Female'), ('A', 'Male'), ('B', 'Female'), ('B', 'Male'),
       ('C', 'Female'), ('C', 'Male'), ('D', 'Female'), ('D', 'Male')],
      dtype=object)

 df_multi.columns.values

array([('Height', 'Freshman'), ('Height', 'Senior'),
       ('Height', 'Sophomore'), ('Height', 'Junior'),
       ('Weight', 'Freshman'), ('Weight', 'Senior'),
       ('Weight', 'Sophomore'), ('Weight', 'Junior')], dtype=object)

如果想要得到某一層的索引,則需要通過 get_level_values 獲得:

 df_multi.index.get_level_values(0)
 Index(['A', 'A', 'B', 'B', 'C', 'C', 'D', 'D'], dtype='object', name='School')

但對于索引而言,無論是單層還是多層,用戶都無法通過 index_obj[0] = item 的方式來修改元素,也不能通過 index_name[0] = new_name 的方式來修改名字,關(guān)于如何修改這些屬性的話題將在第三節(jié)被討論。

  1. 多級索引中的loc索引器
    熟悉了結(jié)構(gòu)后,現(xiàn)在回到原表,將學(xué)校和年級設(shè)為索引,此時的行為多級索引,列為單級索引,由于默認(rèn)狀態(tài)的列索引不含名字,因此對應(yīng)于剛剛圖中 Indicator 和 Grade 的索引名位置是空缺的。
 df_multi = df.set_index(['School', 'Grade'])

 df_multi.head()
 
                                                   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

由于多級索引中的單個元素以元組為單位,因此之前在第一節(jié)介紹的 loc 和 iloc 方法完全可以照搬,只需把標(biāo)量的位置替換成對應(yīng)的元組,不過在索引前最好對 MultiIndex 進(jìn)行排序以避免性能警告:

 df_multi = df_multi.sort_index()

 df_multi.loc[('Fudan University', 'Junior')].head()
 
                                  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

 df_multi.loc[[('Fudan University', 'Senior'),
   ....:               ('Shanghai Jiao Tong University', 'Freshman')]].head()
   ....: 

                                    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

 df_multi.loc[df_multi.Weight > 70].head() # 布爾列表也是可用的

                                     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

 df_multi.loc[lambda x:('Fudan University','Junior')].head()

                                  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

此外,在多級索引中的元組有一種特殊的用法,可以對多層的元素進(jìn)行交叉組合后索引,但同時需要指定 loc 的列,全選則用 : 表示。其中,每一層需要選中的元素用列表存放,傳入 loc 的形式為 [(level_0_list, level_1_list), cols] 。例如,想要得到所有北大和復(fù)旦的大二大三學(xué)生,可以如下寫出:

 res = df_multi.loc[(['Peking University', 'Fudan University'],
   ....:                     ['Sophomore', 'Junior']), :]
   ....: 

 res.head()
 
                                     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

 res.shape
 (33, 4)

下面的語句和上面類似,但仍然傳入的是元素(這里為元組)的列表,它們的意義是不同的,表示的是選出北大的大三學(xué)生和復(fù)旦的大二學(xué)生:

 res = df_multi.loc[[('Peking University', 'Junior'),
   ....:                     ('Fudan University', 'Sophomore')]]
   ....: 

 res.head()
 
                                   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

 res.shape
(16, 4)
  1. IndexSlice對象
    前面介紹的方法,即使在索引不重復(fù)的時候,也只能對元組整體進(jìn)行切片,而不能對每層進(jìn)行切片,也不允許將切片和布爾列表混合使用,引入 IndexSlice 對象就能解決這個問題。 Slice 對象一共有兩種形式,第一種為 loc[idx[,]] 型,第二種為 loc[idx[,],idx[,]] 型,下面將進(jìn)行介紹。為了方便演示,下面構(gòu)造一個 索引不重復(fù)的 DataFrame :
 np.random.seed(0)

 L1,L2 = ['A','B','C'],['a','b','c']

 mul_index1 = pd.MultiIndex.from_product([L1,L2],names=('Upper', 'Lower'))

L3,L4 = ['D','E','F'],['d','e','f']

mul_index2 = pd.MultiIndex.from_product([L3,L4],names=('Big', 'Small'))

df_ex = pd.DataFrame(np.random.randint(-9,10,(9,9)),
   ....:                     index=mul_index1,
   ....:                     columns=mul_index2)
   ....: 

df_ex

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 對象,先要進(jìn)行定義:

 idx = pd.IndexSlice

【a】 loc[idx[,]] 型
這種情況并不能進(jìn)行多層分別切片,前一個 * 表示行的選擇,后一個 * 表示列的選擇,與單純的 loc 是類似的:

 df_ex.loc[idx['C':, ('D', 'f'):]]

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

另外,也支持布爾序列的索引:

 df_ex.loc[idx[:'A', lambda x:x.sum()>0]] # 列和大于0

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)行切片,前一個 idx 指代的是行索引,后一個是列索引。

 df_ex.loc[idx[:'A', 'b':], idx['E':, 'e':]]
 
Big          E     F   
Small        e  f  e  f
Upper Lower            
A     b     -2  5 -4  4
      c      6  6  9 -6
  1. 多級索引的構(gòu)造
    前面提到了多級索引表的結(jié)構(gòu)和切片,那么除了使用 set_index 之外,如何自己構(gòu)造多級索引呢?常用的有 from_tuples, from_arrays, from_product 三種方法,它們都是 pd.MultiIndex 對象下的函數(shù)。

from_tuples 指根據(jù)傳入由元組組成的列表進(jìn)行構(gòu)造:

my_tuple = [('a','cat'),('a','dog'),('b','cat'),('b','dog')]

pd.MultiIndex.from_tuples(my_tuple, names=['First','Second'])
 
MultiIndex([('a', 'cat'),
            ('a', 'dog'),
            ('b', 'cat'),
            ('b', 'dog')],
           names=['First', 'Second'])

from_arrays 指根據(jù)傳入列表中,對應(yīng)層的列表進(jìn)行構(gòu)造:

 my_array = [list('aabb'), ['cat', 'dog']*2]

 pd.MultiIndex.from_arrays(my_array, names=['First','Second'])
 
MultiIndex([('a', 'cat'),
            ('a', 'dog'),
            ('b', 'cat'),
            ('b', 'dog')],
           names=['First', 'Second'])

from_product 指根據(jù)給定多個列表的笛卡爾積進(jìn)行構(gòu)造:

my_list1 = ['a','b']

 my_list2 = ['cat','dog']

pd.MultiIndex.from_product([my_list1,
   .....:                             my_list2],
   .....:                            names=['First','Second'])
   .....: 

MultiIndex([('a', 'cat'),
            ('a', 'dog'),
            ('b', 'cat'),
            ('b', 'dog')],
           names=['First', 'Second'])

三、索引的常用方法

  1. 索引層的交換和刪除
    為了方便理解交換的過程,這里構(gòu)造一個三級索引的例子:
 np.random.seed(0)

 L1,L2,L3 = ['A','B'],['a','b'],['alpha','beta']

mul_index1 = pd.MultiIndex.from_product([L1,L2,L3],
   .....:              names=('Upper', 'Lower','Extra'))
   .....: 

 L4,L5,L6 = ['C','D'],['c','d'],['cat','dog']

 mul_index2 = pd.MultiIndex.from_product([L4,L5,L6],
   .....:              names=('Big', 'Small', 'Other'))
   .....: 

 df_ex = pd.DataFrame(np.random.randint(-9,10,(8,8)),
   .....:                         index=mul_index1,
   .....:                         columns=mul_index2)
   .....: 

 df_ex
 
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 完成,前者只能交換兩個層,而后者可以交換任意層,兩者都可以指定交換的是軸是哪一個,即行索引或列索引:

 df_ex.swaplevel(0,2,axis=1).head() # 列索引的第一層和第三層交換
 
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

 df_ex.reorder_levels([2,0,1],axis=0).head() # 列表數(shù)字指代原來索引中的層
 
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

若想要刪除某一層的索引,可以使用 droplevel 方法:

 df_ex.droplevel(1,axis=1)
 
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

 df_ex.droplevel([0,1],axis=0)
 
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
  1. 索引屬性的修改
    通過 rename_axis 可以對索引層的名字進(jìn)行修改,常用的修改方式是傳入字典的映射:
 df_ex.rename_axis(index={'Upper':'Changed_row'},
   .....:                   columns={'Other':'Changed_Col'}).head()
   .....: 
 
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 可以對索引的值進(jìn)行修改,如果是多級索引需要指定修改的層號 level :

 df_ex.rename(columns={'cat':'not_cat'},
   .....:              level=2).head()
   .....: 
 
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ù),其輸入值就是索引元素:

 df_ex.rename(index=lambda x:str.upper(x),
   .....:              level=2).head()
   .....: 
 
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

對于整個索引的元素替換,可以利用迭代器實現(xiàn):

 new_values = iter(list('abcdefgh'))

 df_ex.rename(index=lambda x:next(new_values),
   .....:              level=2)
   .....: 
 
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

若想要對某個位置的元素進(jìn)行修改,在單層索引時容易實現(xiàn),即先取出索引的 values 屬性,再給對得到的列表進(jìn)行修改,最后再對 index 對象重新賦值。但是如果是多級索引的話就有些麻煩,一個解決的方案是先把某一層索引臨時轉(zhuǎn)為表的元素,然后再進(jìn)行修改,最后重新設(shè)定為索引,下面一節(jié)將介紹這些操作。

另外一個需要介紹的函數(shù)是 map ,它是定義在 Index 上的方法,與前面 rename 方法中層的函數(shù)式用法是類似的,只不過它傳入的不是層的標(biāo)量值,而是直接傳入索引的元組,這為用戶進(jìn)行跨層的修改提供了遍歷。例如,可以等價地寫出上面的字符串轉(zhuǎn)大寫的操作:

 df_temp = df_ex.copy()

 new_idx = df_temp.index.map(lambda x: (x[0],
   .....:                                        x[1],
   .....:                                        str.upper(x[2])))
   .....: 

 df_temp.index = new_idx

 df_temp.head()
 
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 的另一個使用方法是對多級索引的壓縮,這在第四章和第五章的一些操作中是有用的:

 df_temp = df_ex.copy()

 new_idx = df_temp.index.map(lambda x: (x[0]+'-'+
   .....:                                        x[1]+'-'+
   .....:                                        x[2]))
   .....: 

 df_temp.index = new_idx

 df_temp.head() # 單層索引

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

同時,也可以反向地展開:

 new_idx = df_temp.index.map(lambda x:tuple(x.split('-')))

 df_temp.index = new_idx

 df_temp.head() # 三層索引

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
  1. 索引的設(shè)置與重置
    為了說明本節(jié)的函數(shù),下面構(gòu)造一個新表:
 df_new = pd.DataFrame({'A':list('aacd'),
   .....:                        'B':list('PQRT'),
   .....:                        'C':[1,2,3,4]})
   .....: 

 df_new

   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)層:

 df_new.set_index('A')

   B  C
A      
a  P  1
a  Q  2
c  R  3
d  T  4

 df_new.set_index('A', append=True)
 
     B  C
  A      
0 a  P  1
1 a  Q  2
2 c  R  3
3 d  T  4

可以同時指定多個列作為索引:

 df_new.set_index(['A', 'B'])

     C
A B   
a P  1
  Q  2
c R  3
d T  4

如果想要添加索引的列沒有出現(xiàn)再其中,那么可以直接在參數(shù)中傳入相應(yīng)的 Series :

 my_index = pd.Series(list('WXYZ'), name='D')

 df_new = df_new.set_index(['A', my_index])

 df_new
 
     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 ,表示是否要把去掉的索引層丟棄,而不是添加到列中:

 df_new.reset_index(['D'])
 
   D  B  C
A         
a  W  P  1
a  X  Q  2
c  Y  R  3
d  Z  T  4

 df_new.reset_index(['D'], drop=True)
 
   B  C
A      
a  P  1
a  Q  2
c  R  3
d  T  4

如果重置了所有的索引,那么 pandas 會直接重新生成一個默認(rèn)索引:

 df_new.reset_index()

   A  D  B  C
0  a  W  P  1
1  a  X  Q  2
2  c  Y  R  3
3  d  Z  T  4
  1. 索引的變形
    在某些場合下,需要對索引做一些擴(kuò)充或者剔除,更具體地要求是給定一個新的索引,把原表中相應(yīng)的索引對應(yīng)元素填充到新索引構(gòu)成的表中。例如,下面的表中給出了員工信息,需要重新制作一張新的表,要求增加一名員工的同時去掉身高列并增加性別列:
 df_reindex = pd.DataFrame({"Weight":[60,70,80],
   .....:                            "Height":[176,180,179]},
   .....:                            index=['1001','1003','1002'])
   .....: 

 df_reindex
 
      Weight  Height
1001      60     176
1003      70     180
1002      80     179

 df_reindex.reindex(index=['1001','1002','1003','1004'],
   .....:                    columns=['Weight','Gender'])
   .....: 
 
      Weight  Gender
1001    60.0     NaN
1002    80.0     NaN
1003    70.0     NaN
1004     NaN     NaN

這種需求常出現(xiàn)在時間序列索引的時間點(diǎn)填充以及 ID 編號的擴(kuò)充。另外,需要注意的是原來表中的數(shù)據(jù)和新表中會根據(jù)索引自動對其,例如原先的1002號位置在1003號之后,而新表中相反,那么 reindex 中會根據(jù)元素對其,與位置無關(guān)。

還有一個與 reindex 功能類似的函數(shù)是 reindex_like ,其功能是仿照傳入的表的索引來進(jìn)行被調(diào)用表索引的變形。例如,現(xiàn)在以及存在一張表具備了目標(biāo)索引的條件,那么上述功能可以如下等價地寫出:

 df_existed = pd.DataFrame(index=['1001','1002','1003','1004'],
   .....:                           columns=['Weight','Gender'])
   .....: 

 df_reindex.reindex_like(df_existed)
 
      Weight  Gender
1001    60.0     NaN
1002    80.0     NaN
1003    70.0     NaN
1004     NaN     NaN

四、索引運(yùn)算

  1. 集合的運(yùn)算法則
    經(jīng)常會有一種利用集合運(yùn)算來取出符合條件行的需求,例如有兩張表 A 和 B ,它們的索引都是員工編號,現(xiàn)在需要篩選出兩表索引交集的所有員工信息,此時通過 Index 上的運(yùn)算操作就很容易實現(xiàn)。
    不過在此之前,不妨先復(fù)習(xí)一下常見的四種集合運(yùn)算:
SA.intersection(SB)SA.union(SB)SA.difference(SB)SA.symmetric_difference(SB)=SA∩SB?{x|x∈SAandx∈SB}=SA∪SB?{x|x∈SAorx∈SB}=SA?SB?{x|x∈SAandx?SB}=SA△SB?{x|x∈SA∪SB?SA∩SB}
  1. 一般的索引運(yùn)算
    由于集合的元素是互異的,但是索引中可能有相同的元素,先用 unique 去重后再進(jìn)行運(yùn)算。下面構(gòu)造兩張最為簡單的示例表進(jìn)行演示:
 df_set_1 = pd.DataFrame([[0,1],[1,2],[3,4]],
   .....:                         index = pd.Index(['a','b','a'],name='id1'))
   .....: 

 df_set_2 = pd.DataFrame([[4,5],[2,6],[7,1]],
   .....:                         index = pd.Index(['b','b','c'],name='id2'))
   .....: 

 id1, id2 = df_set_1.index.unique(), df_set_2.index.unique()

 id1.intersection(id2)
 Index(['b'], dtype='object')

 id1.union(id2)
Index(['a', 'b', 'c'], dtype='object')

 id1.difference(id2)
 Index(['a'], dtype='object')

 id1.symmetric_difference(id2)
 Index(['a', 'c'], dtype='object')

上述的四類運(yùn)算還可以用等價的符號表示代替如下:

 id1 & id2
 Index(['b'], dtype='object')

 id1 | id2
 Index(['a', 'b', 'c'], dtype='object')

 (id1 ^ id2) & id1
 Index(['a'], dtype='object')

 id1 ^ id2 # ^符號即對稱差
 Index(['a', 'c'], dtype='object')

若兩張表需要做集合運(yùn)算的列并沒有被設(shè)置索引,一種辦法是先轉(zhuǎn)成索引,運(yùn)算后再恢復(fù),另一種方法是利用 isin 函數(shù),例如在重置索引的第一張表中選出id列交集的所在行:

 df_set_in_col_1 = df_set_1.reset_index()

 df_set_in_col_2 = df_set_2.reset_index()

 df_set_in_col_1
 
  id1  0  1
0   a  0  1
1   b  1  2
2   a  3  4

 df_set_in_col_2
 
  id2  0  1
0   b  4  5
1   b  2  6
2   c  7  1

df_set_in_col_1[df_set_in_col_1.id1.isin(df_set_in_col_2.id2)]
 
  id1  0  1
1   b  1  2
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容