简介

选择DataFrame 或 Series 中的特定值去处理几乎是每个数据处理中都会用到的。所以,一件重要的事情就是你需要学习如何从数据中快速、高效的选取你要处理的数据。

原生选择器

原生对象提供了一种索引数据的非常好的方法,Pandas 把他们移植过来,可以帮我们使数据的索引变得非常简单。
仍然以wine_reviews的DataFrame为例:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews)
  1. country ... winery
  2. 0 Italy ... Nicosia
  3. 1 Portugal ... Quinta dos Avidagos
  4. ... ... ... ...
  5. 129969 France ... Domaine Marcel Deiss
  6. 129970 France ... Domaine Schoffit
  7. [129971 rows x 13 columns]

在python中,我们通过访问其一个属性访问对象的性质,例如,一个对象“书”,可能有“title”属性,我们可以通过“book.title”调用, pandas 的 DataFrame 中的列以相同的方式工作。这样,我们就可以访问 reviews 中
country 属性:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews.country)
  1. 0 Italy
  2. 1 Portugal
  3. ...
  4. 129969 France
  5. 129970 France
  6. Name: country, Length: 129971, dtype: object

如果有们有一个python的字典,我们可以通过使用索引 ([]) 的操作方法访问其值,在DataFrame中也可以使用这个方法:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews['country'])
  1. 0 Italy
  2. 1 Portugal
  3. ...
  4. 129969 France
  5. 129970 France
  6. Name: country, Length: 129971, dtype: object

这里有两种访问 DataFrame 中 特定序列Series 的方法,二者在语法上没有优劣之分,但是索引运算符[]的优点是可以通过保留字符串处理列名(例如,如果我们有一个country providence列,reviews.country providence将不起作用)。
是否觉得pandas Series看起来象一个字典,比字典更漂亮,所以不必惊奇,我们仅仅再次使用索引([])便 可以钻取到单个特定值。

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews['country'][0]) # Italy

pandas 中的索引

索引操作符和属性选择非常好用,因为他们工作机理与在Python其他生态系统中一样。作为新手,这使他们非常容易被想起和使用。然尔,Pandas也有自己特有的访问器操作符:loc 和 iloc. 当需要更高级的操作时,应该首先想到应用这两种方法。

基于选择的索引

Pandas 的索引工作于两个范式之一,第一个是基于选择的索引:基于其在数据中的数字化的位置选择数据,iloc方法基于这种范式。
选择DtaFrame的第一行数据,可以使用以下的方法:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews.iloc[0])
  1. country Italy
  2. description Aromas include tropical fruit, broom, brimston...
  3. ...
  4. variety White Blend
  5. winery Nicosia
  6. Name: 0, Length: 13, dtype: object

loc 和 iloc 方法都是先行后列,这与原生Python中的用法相反,后者是先列后行。这意味着检索行稍微容易一些,检索列稍微难一些。要使用iloc获取序号为 0 的列,我们可以执行以下操作:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews.iloc[:, 0])
  1. 0 Italy
  2. 1 Portugal
  3. ...
  4. 129969 France
  5. 129970 France
  6. Name: country, Length: 129971, dtype: object

就其本身而言,:操作符也来自本地Python,意思是“全部”。但是,当与其他选择器结合使用时,它可以用于指示值的范围。例如,要从第一行、第二行和第三行中选择country列,我们将执行以下操作:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews.iloc[:3, 0])
  1. 0 Italy
  2. 1 Portugal
  3. 2 US
  4. Name: country, dtype: object

如果想只选择第二个和第三个条目,可以执行以下代码:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews.iloc[1:3, 0])
  1. 1 Portugal
  2. 2 US
  3. Name: country, dtype: object

也可以传递一个列表做为参数:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews.iloc[[0, 1, 2], 0])
  1. 0 Italy
  2. 1 Portugal
  3. 2 US
  4. Name: country, dtype: object

值得注意的是,负数也可以用于选择。这将从值的末尾开始向前计数。例如,取数据集的最后五个元素。

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews.iloc[-5:])
  1. country ... winery
  2. 129966 Germany ... Dr. H. Thanisch (Erben Müller-Burggraef)
  3. 129967 US ... Citation
  4. 129968 France ... Domaine Gresser
  5. 129969 France ... Domaine Marcel Deiss
  6. 129970 France ... Domaine Schoffit
  7. [5 rows x 13 columns]

基于标签的选择

属性选择的第二个范式是 loc 操作符:基于标签的选择。在这个范式中,重要的是数据索引的值,而不是它的位置。
例如,要获取reviews中的第一个条目,可以执行以下操作:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews.loc[0, 'country']) # Italy

因为 iloc 忽略了数据集的索引,所以在概念上比loc简单。在使用iloc时,将数据集视为一个大矩阵(一个元素为列表的列表),一个必须按位置索引的矩阵。
相反,loc使用索引中的信息来完成它的工作。由于数据集通常具有有意义的索引,因此使用loc通常更容易执行操作。
例如,下面 这个实例使用 loc 操作更容易完成:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews.loc[:, ['taster_name', 'taster_twitter_handle', 'points']]) # 根据列标签指定列,:表示所有行
  1. taster_name taster_twitter_handle points
  2. 0 Kerin OKeefe @kerinokeefe 87
  3. 1 Roger Voss @vossroger 87
  4. 2 Paul Gregutt @paulgwine 87
  5. 3 Alexander Peartree NaN 87
  6. 4 Paul Gregutt @paulgwine 87
  7. ... ... ... ...
  8. 129966 Anna Lee C. Iijima NaN 90
  9. 129967 Paul Gregutt @paulgwine 90
  10. 129968 Roger Voss @vossroger 90
  11. 129969 Roger Voss @vossroger 90
  12. 129970 Roger Voss @vossroger 90
  13. [129971 rows x 3 columns]

loc和iloc之间选择原则
当在loc和iloc之间选择或转换时,应该时刻牢记这两种索引方法在使用中的轻微不同。
iloc使用Python 标准库索引方案,其中包含范围的第一个元素,排除最后一个元素。所以iloc[0:10]将选择条目0,…,9。然而 loc 包含索引边界,所以loc[0:10]将选择条目0,…,10。
loc可以索引任何python标准库数据类型:例如 字符串。如果我们有一个DataFrame的索引值为“Apples,…,Potatoes,…,”,我们希望选择““Apples”和“Potatoes”之间所有按字母表顺序排列的水果选项”,那么索引df.loc[‘Apples’:’Potatoes]比索引df.loc[‘Apples’,’Potatoet]要方便得多(在字母表中的s后面是t)。
当 DataFrame 索引是一个简单的数字列表(例如0,…,1000)时,容易令人困惑。在这种情况下,df.iloc[0:1000]将返回1000项数据(不包括右边界),而 df.loc[0:1000] 将返回1001个项数据(包括右边界),所以要使用 loc 获得 1000 个元素,需要右边界减少1,使用 df.loc[0:999],这样才能使 loc 的语义与 iloc 相同。

索引的操纵

基于标签的选择驱动力来自于索引中的标签,而且,我们使用的索引不是不可改变的,可以用任何我们认为合适的方式操纵索引。
set_index() 方法可用于完成这项工作,下面是将索引设置到标题字段的操作:

import pandas as pd

reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
print(reviews.head())
                                                     country  ...                winery
title                                                         ...                      
Nicosia 2013 Vulkà Bianco  (Etna)                      Italy  ...               Nicosia
Quinta dos Avidagos 2011 Avidagos Red (Douro)       Portugal  ...   Quinta dos Avidagos
...                                                      ...  ...                   ...
Domaine Marcel Deiss 2012 Pinot Gris (Alsace)         France  ...  Domaine Marcel Deiss
Domaine Schoffit 2012 Lieu-dit Harth Cuvée Caro...    France  ...      Domaine Schoffit

[129971 rows x 12 columns]
print(reviews.set_index("title").head())
    country  ...               winery
0     Italy  ...              Nicosia
1  Portugal  ...  Quinta dos Avidagos
2        US  ...            Rainstorm
3        US  ...           St. Julian
4        US  ...         Sweet Cheeks

[5 rows x 13 columns]
print(reviews.set_index("country").head())
                                                description  ...               winery
country                                                      ...                     
Italy     Aromas include tropical fruit, broom, brimston...  ...              Nicosia
Portugal  This is ripe and fruity, a wine that is smooth...  ...  Quinta dos Avidagos
US        Tart and snappy, the flavors of lime flesh and...  ...            Rainstorm
US        Pineapple rind, lemon pith and orange blossom ...  ...           St. Julian
US        Much like the regular bottling from 2012, this...  ...         Sweet Cheeks

[5 rows x 12 columns]

如果你想为数据集提供一个比当前索引更好的索引,这个方法将会非常有用。

条件选择

到目前为止,我们一直在使用 DataFrame 本身的结构属性为数据的各个方面编制索引。然而,要对数据做有趣的分析时,我们通常需要根据条件提出问题。
例如,假设我们对产于意大利的好于平均质量的葡萄酒特别感兴趣,我们可以先检查每种葡萄酒是否产自意大利:

import pandas as pd

reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
print(reviews.country == 'Italy')
0          True
1         False
2         False
          ...  
129969    False
129970    False
Name: country, Length: 129971, dtype: bool
print(reviews.head().country == 'Italy')
# 或
print(reviews.head()['country'] == 'Italy')
0     True
1    False
2    False
3    False
4    False
Name: country, dtype: bool

这个操作根据每个记录的国家属性产生了一系列的值为 真或假 的布尔值。然后可以在loc内部使用此结果来选择相关数据:

import pandas as pd

reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
print(reviews.loc[reviews.country == 'Italy'])
       country  ...                           winery
0        Italy  ...                          Nicosia
6        Italy  ...                  Terre di Giurfo
13       Italy  ...              Masseria Setteporte
22       Italy  ...               Baglio di Pianetto
24       Italy  ...                        Canicattì
...        ...  ...                              ...
129929   Italy  ...             Col Vetoraz Spumanti
129943   Italy  ...  Baglio del Cristo di Campobello
129947   Italy  ...         Feudo Principi di Butera
129961   Italy  ...                              COS
129962   Italy  ...                         Cusumano

[19540 rows x 13 columns]

这个DataFrame 有大约20000行,原始数据有13万行,这意味着大约15%的葡萄酒来自意大利。
我们还想知道哪些比平均水平好,葡萄酒被评为80到100分,所以这可能意味着葡萄酒达到90分。
我们可以使用与号(&)将这两个条件结合起来,同时满足产自意大利且评分高于90这两个条件的数据才会被筛选出来,这样得到返回的数据为6648行:

import pandas as pd

reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
print(reviews.loc[(reviews.country == 'Italy') & (reviews.points >= 90)])
       country  ...                           winery
120      Italy  ...                          Ceretto
130      Italy  ...                          Ceretto
133      Italy  ...             Poderi Luigi Einaudi
135      Italy  ...                  Giacomo Ascheri
140      Italy  ...                     Poderi Colla
...        ...  ...                              ...
129929   Italy  ...             Col Vetoraz Spumanti
129943   Italy  ...  Baglio del Cristo di Campobello
129947   Italy  ...         Feudo Principi di Butera
129961   Italy  ...                              COS
129962   Italy  ...                         Cusumano

[6648 rows x 13 columns]

如果我们会买任何意大利产的 或 是评级高于平均水平的葡萄酒,可以使用管道符(|),满足其中一个条件的数据就会被返回,可以得到61937行数据:

import pandas as pd

reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
print(reviews.loc[(reviews.country == 'Italy') | (reviews.points >= 90)])
print(reviews.loc[(reviews.country == 'Italy') & (reviews.points >= 90) & reviews.price.notnull()])
        country  ...                                    winery
0         Italy  ...                                   Nicosia
6         Italy  ...                           Terre di Giurfo
13        Italy  ...                       Masseria Setteporte
22        Italy  ...                        Baglio di Pianetto
24        Italy  ...                                 Canicattì
...         ...  ...                                       ...
129966  Germany  ...  Dr. H. Thanisch (Erben Müller-Burggraef)
129967       US  ...                                  Citation
129968   France  ...                           Domaine Gresser
129969   France  ...                      Domaine Marcel Deiss
129970   France  ...                          Domaine Schoffit

[61937 rows x 13 columns]

同时满足产自意大利、评分高于90、且价格标签完整三个条件的数据仅有5864条记录:

print(reviews.loc[(reviews.country == 'Italy') & (reviews.points >= 90) & reviews.price.notnull()])
       country  ...                           winery
120      Italy  ...                          Ceretto
130      Italy  ...                          Ceretto
133      Italy  ...             Poderi Luigi Einaudi
135      Italy  ...                  Giacomo Ascheri
140      Italy  ...                     Poderi Colla
...        ...  ...                              ...
129929   Italy  ...             Col Vetoraz Spumanti
129943   Italy  ...  Baglio del Cristo di Campobello
129947   Italy  ...         Feudo Principi di Butera
129961   Italy  ...                              COS
129962   Italy  ...                         Cusumano

[5864 rows x 13 columns]

Pandas附带了一些内置的条件选择器,这里重点介绍其中的两个:
第一个是 isin:
isin允许选择值“在”值的列表中的数据,例如,可以使用它来选择仅来自意大利或法国的葡萄酒,有41633条数据:

import pandas as pd

reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
print(reviews.loc[reviews.country.isin(['Italy', 'France'])])
       country  ...                   winery
0        Italy  ...                  Nicosia
6        Italy  ...          Terre di Giurfo
7       France  ...                 Trimbach
9       France  ...       Jean-Baptiste Adam
11      France  ...               Leon Beyer
...        ...  ...                      ...
129964  France  ...          Domaine Ehrhart
129965  France  ...  Domaine Rieflé-Landmann
129968  France  ...          Domaine Gresser
129969  France  ...     Domaine Marcel Deiss
129970  France  ...         Domaine Schoffit

[41633 rows x 13 columns]

第二个是 isnull(以及它的同伴 notnull):
这两方法允许突出显示为空(NaN或不是NaN)的值。例如,要过滤掉数据集中缺少价格标签的葡萄酒,可以做以下操作:

import pandas as pd

reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
print(reviews.loc[reviews.price.notnull()])
         country  ...                                    winery
1       Portugal  ...                       Quinta dos Avidagos
2             US  ...                                 Rainstorm
3             US  ...                                St. Julian
4             US  ...                              Sweet Cheeks
5          Spain  ...                                    Tandem
...          ...  ...                                       ...
129966   Germany  ...  Dr. H. Thanisch (Erben Müller-Burggraef)
129967        US  ...                                  Citation
129968    France  ...                           Domaine Gresser
129969    France  ...                      Domaine Marcel Deiss
129970    France  ...                          Domaine Schoffit

[120975 rows x 13 columns]
print(reviews.loc[reviews.price.isnull()])
         country  ...                winery
0          Italy  ...               Nicosia
13         Italy  ...   Masseria Setteporte
30        France  ...  Domaine de la Madone
31         Italy  ...    Duca di Salaparuta
32         Italy  ...    Duca di Salaparuta
...          ...  ...                   ...
129844     Italy  ...               Caparzo
129860  Portugal  ...     Quinta da Pacheca
129863  Portugal  ...              Seacampo
129893     Italy  ...          Le Mandolare
129964    France  ...       Domaine Ehrhart

[8996 rows x 13 columns]

数据赋值

另一方面,将数据赋值给数据帧很容易实现,可以指定一个常量值:

import pandas as pd

reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
reviews['critic'] = 'everyone' # 将 'critic' 列的值都改为'everyone'
print(reviews['critic'])
0         everyone
1         everyone
2         everyone
            ...   
129969    everyone
129970    everyone
Name: critic, Length: 129971, dtype: object

或者使用一个可迭代的值:

import pandas as pd

reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
reviews['index_backwards'] = range(len(reviews), 0, -1)  # 给该列数据赋逆序的序号值
print(reviews['index_backwards'])
0         129971
1         129970
2         129969
3         129968
4         129967
           ...  
129966         5
129967         4
129968         3
129969         2
129970         1
Name: index_backwards, Length: 129971, dtype: int32