介绍

Maps允许我们一次把DataFrame 或 Series中的一列数据转换为一个值。但是,通常我们希望对数据进行分组,然后对数据所在的组执行特定的操作。

正如您将了解到的,我们使用 groupby() 函数来完成这个操作。本节我们还将讨论一些例如更复杂的 DataFrames 索引方法,以及如何对数据排序等其他主题。

分组分析

到目前为止,我们一直在大量使用的一个函数是 value_counts() 函数。我们可以通过执行以下操作完成value_counts() 函数所实现的功能:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews.groupby('points').points.count())

每个分值的出现次数

  1. points
  2. 80 397
  3. 81 692
  4. 82 1836
  5. 83 3025
  6. 84 6480
  7. 85 9530
  8. 86 12600
  9. 87 16933
  10. 88 17207
  11. 89 12226
  12. 90 15410
  13. 91 11359
  14. 92 9613
  15. 93 6489
  16. 94 3758
  17. 95 1535
  18. 96 523
  19. 97 229
  20. 98 77
  21. 99 33
  22. 100 19
  23. Name: points, dtype: int64

groupby() 先创建一系列reviews的分组,将相同的分值的葡萄酒分配到一组。然后,针对每一个组,我们抓取points() 列并计算葡萄酒品牌出现的次数。value_counts() 是groupby() 操作的一个快捷方式。
我们可以对这些数据使用以前使用过的任何汇总函数。例如,要获得每个评分值组中最便宜的葡萄酒,我们可以执行以下操作:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews.groupby('points').price.min())
  1. points
  2. 80 5.0
  3. 81 5.0
  4. 82 4.0
  5. 83 4.0
  6. 84 4.0
  7. 85 4.0
  8. 86 4.0
  9. 87 5.0
  10. 88 6.0
  11. 89 7.0
  12. 90 8.0
  13. 91 7.0
  14. 92 11.0
  15. 93 12.0
  16. 94 13.0
  17. 95 20.0
  18. 96 20.0
  19. 97 35.0
  20. 98 50.0
  21. 99 44.0
  22. 100 80.0
  23. Name: price, dtype: float64

您可以将生成的每个组看成 DataFrame 中的只包括数据与其对应值的一个切片。我们可以直接使用apply() 方法访问这个 DataFrame ,然后我们可以以任何我们认为合适的方式操作数据。例如,下面方法可以从数据集中的每个酒庄中选择评价排名第一的葡萄酒的名称:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews.groupby('winery').apply(lambda df: df.title.iloc[0]))
  1. winery
  2. 1+1=3 1+1=3 NV Rosé Sparkling (Cava)
  3. 10 Knots 10 Knots 2010 Viognier (Paso Robles)
  4. 100 Percent Wine 100 Percent Wine 2015 Moscato (California)
  5. 1000 Stories 1000 Stories 2013 Bourbon Barrel Aged Zinfande...
  6. 1070 Green 1070 Green 2011 Sauvignon Blanc (Rutherford)
  7. ...
  8. Órale Órale 2011 Cabronita Red (Santa Ynez Valley)
  9. Öko Öko 2013 Made With Organically Grown Grapes Ma...
  10. Ökonomierat Rebholz Ökonomierat Rebholz 2007 Von Rotliegenden Spät...
  11. àMaurice àMaurice 2013 Fred Estate Syrah (Walla Walla V...
  12. Štoka Štoka 2009 Izbrani Teran (Kras)
  13. Length: 16757, dtype: object
  1. GroupBy.apply(func, *args, **kwargs)

apply方法将func函数应用到分组上并将结果组合在一起。
传递给apply()函数的第一个参数必须是dataframe,返回dataframe、Series或标量。然后apply将负责将结果组合到一个单独的数据帧或序列中。因此,apply是一种高度灵活的分组方法。

apply()是一种非常灵活的方法,它的缺点是比使用agg和transform等方法慢一些,Pandas提供了特殊应用方向 上提供了更广泛应用的方法可以提供更快高的效率,在考虑使用apply之前应先考虑使用它们。

对于更细粒度的控制,还可以按多个列进行分组。举个例子,下面是如何根据国家和省份挑选最好的葡萄酒:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews.groupby(['country', 'province']).apply(lambda df: df.loc[df.points.idxmax()]))
  1. DataFrame.idxmax(axis=0, skipna=True)

返回请求轴方向上第一次出现最大值的索引。
axis{0 or ‘index’, 1 or ‘columns’}, default 0
要使用的轴。0或“index”表示行,1或“columns”表示列。
skipnabool, default True
排除NA/null值。如果整行/列为NA,则结果为NA(缺失值)。
返回值Series

  1. Series.idxmax(axis=0, skipna=True, *args, **kwargs)

返回最大值的行标签, 如果多个值等于最大值,则返回具有该值的第一行标签。

  1. country ... winery
  2. country province ...
  3. Argentina Mendoza Province Argentina ... Bodega Catena Zapata
  4. Other Argentina ... Colomé
  5. Armenia Armenia Armenia ... Van Ardi
  6. Australia Australia Other Australia ... Marquis Philips
  7. New South Wales Australia ... De Bortoli
  8. ... ... ... ...
  9. Uruguay Juanico Uruguay ... Familia Deicas
  10. Montevideo Uruguay ... Bouza
  11. Progreso Uruguay ... Pisano
  12. San Jose Uruguay ... Castillo Viejo
  13. Uruguay Uruguay ... Narbona
  14. [425 rows x 13 columns]

另一个值得一提的可用于groupby() 方法是agg()函数,它允许您在 DataFrame 上同时运行一堆不同的函数。例如,下面代码可以生成数据集的简单统计摘要:

  1. DataFrame.agg(func=None, axis=0, *args, **kwargs)

Parameters:func:function, str, list or dict
用于聚合数据的函数。如果是函数,则必须在传递DataFrame或传递给DataFrame.apply时使用
可接受的组合包括:
函数
表示函数名的字符串
函数和/或函数名的列表,例如[np.sum,’mean’]
轴标签的字典->函数、函数名或此类列表。

  1. pandas.Series.agg

在指定轴上使用一个或多个操作进行聚合。(len表示分组后组中元素的个数,min最小值,max最大值)

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. print(reviews.groupby(['country']).price.agg([len, min, max]))
  1. len min max
  2. country
  3. Argentina 3800.0 4.0 230.0
  4. Armenia 2.0 14.0 15.0
  5. Australia 2329.0 5.0 850.0
  6. Austria 3345.0 7.0 1100.0
  7. Bosnia and Herzegovina 2.0 12.0 13.0
  8. Brazil 52.0 10.0 60.0
  9. Bulgaria 141.0 8.0 100.0
  10. Canada 257.0 12.0 120.0
  11. Chile 4472.0 5.0 400.0
  12. China 1.0 18.0 18.0
  13. Croatia 73.0 12.0 65.0
  14. Cyprus 11.0 11.0 21.0
  15. Czech Republic 12.0 15.0 45.0
  16. Egypt 1.0 NaN NaN
  17. England 74.0 25.0 95.0
  18. France 22093.0 5.0 3300.0
  19. Georgia 86.0 9.0 40.0
  20. Germany 2165.0 5.0 775.0
  21. Greece 466.0 8.0 79.0
  22. Hungary 146.0 10.0 764.0
  23. India 9.0 10.0 20.0
  24. Israel 505.0 8.0 150.0
  25. Italy 19540.0 5.0 900.0
  26. Lebanon 35.0 13.0 75.0
  27. Luxembourg 6.0 16.0 30.0
  28. Macedonia 12.0 15.0 20.0
  29. Mexico 70.0 8.0 108.0
  30. Moldova 59.0 8.0 42.0
  31. Morocco 28.0 14.0 40.0
  32. New Zealand 1419.0 7.0 130.0
  33. Peru 16.0 10.0 68.0
  34. Portugal 5691.0 5.0 1000.0
  35. Romania 120.0 4.0 320.0
  36. Serbia 12.0 15.0 42.0
  37. Slovakia 1.0 16.0 16.0
  38. Slovenia 87.0 7.0 90.0
  39. South Africa 1401.0 5.0 330.0
  40. Spain 6645.0 4.0 770.0
  41. Switzerland 7.0 21.0 160.0
  42. Turkey 90.0 14.0 120.0
  43. US 54504.0 4.0 2013.0
  44. Ukraine 14.0 6.0 13.0
  45. Uruguay 109.0 10.0 130.0

有效地使用groupby()方法会允许您对数据集执行许多非常强大的操作。

多索引

到目前为止,在我们看到的所有示例中,我们都使用带有单个标签索引的DataFrame或Series对象。groupby() 稍有不同,根据我们运行的操作不同,它有时会导致所谓的多索引。
多索引不同于常规索引,因为它有多个级别。例如:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. countries_reviewed = reviews.groupby(['country', 'province']).description.agg([len])
  4. print(countries_reviewed)
  1. len
  2. country province
  3. Argentina Mendoza Province 3264
  4. Other 536
  5. Armenia Armenia 2
  6. Australia Australia Other 245
  7. New South Wales 85
  8. ... ...
  9. Uruguay Juanico 12
  10. Montevideo 11
  11. Progreso 11
  12. San Jose 3
  13. Uruguay 24
  14. [425 rows x 1 columns]
  1. mi = countries_reviewed.index
  2. print(type(mi)) # <class 'pandas.core.indexes.multi.MultiIndex'>

多索引有一些单索引没有的、能够处理分层结构的方法。它们还需要两个级别的标签来检索值。对于刚接触 pandas的用户来说,处理多索引输出是一个常见的“陷阱””gotcha”。
在pandas文档的multi index/Advanced Selection部分中,将详细介绍多索引的用例以及使用它们的说明。
但是,通常最常用的多索引方法是用于转换回常规索引的方法reset_index() 方法:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. countries_reviewed = reviews.groupby(['country', 'province']).description.agg([len])
  4. print(countries_reviewed.reset_index())
  1. country province len
  2. 0 Argentina Mendoza Province 3264
  3. 1 Argentina Other 536
  4. 2 Armenia Armenia 2
  5. 3 Australia Australia Other 245
  6. 4 Australia New South Wales 85
  7. .. ... ... ...
  8. 420 Uruguay Juanico 12
  9. 421 Uruguay Montevideo 11
  10. 422 Uruguay Progreso 11
  11. 423 Uruguay San Jose 3
  12. 424 Uruguay Uruguay 24
  13. [425 rows x 3 columns]

排序

回头再看一下根据countries_reviewed的结果,我们可以看到分组返回的数据是按索引顺序返回的,而不是按值顺序返回的。也就是说,当输出groupby的结果时,行的顺序取决于索引中的值,而不是数据中的值。
为了得到我们想要的顺序的数据,可以自己排序。sort_values() 方法可以方便的实现这个功能。

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. countries_reviewed = reviews.groupby(['country', 'province']).description.agg([len])
  4. countries_reviewed = countries_reviewed.reset_index()
  5. print(countries_reviewed.sort_values(by='len'))
  1. DataFrame.reset_index(level=None, drop=False, inplace=False, col_level=0, col_fill='')
  2. Series.reset_index(level=None, drop=False, name=None, inplace=False)

重置索引或其级别。
重置DataFrame的索引,并使用默认索引。如果DataFrame具有多重索引,则此方法可以删除一个或多个级别。

  1. country province len
  2. 179 Greece Muscat of Kefallonian 1
  3. 192 Greece Sterea Ellada 1
  4. 194 Greece Thraki 1
  5. 354 South Africa Paardeberg 1
  6. 40 Brazil Serra do Sudeste 1
  7. .. ... ... ...
  8. 409 US Oregon 5373
  9. 227 Italy Tuscany 5897
  10. 118 France Bordeaux 5941
  11. 415 US Washington 8639
  12. 392 US California 36247
  13. [425 rows x 3 columns]

sort_values() 默认为最低值优先的升序排序。然而,大多数情况下,我们需要一个最高的数字排第一的降序排序,可以用指定参数 ascending=False 的方法实现:

  1. DataFrame.sort_values(by, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last', ignore_index=False, key=None)

按任意轴的值排序,默认升序,’quicksort’算法(快速排序算法)。
by:str or list of str 字符串或字符串的列表
要排序的名称或名称列表。

如果轴为0或“index”,则by可能包含索引级别和/或列标签。
如果axis为1或“columns”,则by可能包含列级别和/或索引标签。
在版本0.23.0中更改:允许指定索引或列级别名称。

  1. Series.sort_values(axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last', ignore_index=False, key=None)

按值排序,按某种标准按升序或降序对序列进行排序。

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. countries_reviewed = reviews.groupby(['country', 'province']).description.agg([len])
  4. countries_reviewed = countries_reviewed.reset_index()
  5. print(countries_reviewed.sort_values(by='len', ascending=False))
  1. country province len
  2. 392 US California 36247
  3. 415 US Washington 8639
  4. 118 France Bordeaux 5941
  5. 227 Italy Tuscany 5897
  6. 409 US Oregon 5373
  7. .. ... ... ...
  8. 101 Croatia Krk 1
  9. 247 New Zealand Gladstone 1
  10. 357 South Africa Piekenierskloof 1
  11. 63 Chile Coelemu 1
  12. 149 Greece Beotia 1
  13. [425 rows x 3 columns]

若要按索引值排序,请使用一个类似的方法sort_index(),此方法与 sort_values() 方法具有相同的参数和默认顺序:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. countries_reviewed = reviews.groupby(['country', 'province']).description.agg([len])
  4. print(countries_reviewed.sort_index())
  1. len
  2. country province
  3. Argentina Mendoza Province 3264
  4. Other 536
  5. Armenia Armenia 2
  6. Australia Australia Other 245
  7. New South Wales 85
  8. ... ...
  9. Uruguay Juanico 12
  10. Montevideo 11
  11. Progreso 11
  12. San Jose 3
  13. Uruguay 24
  14. [425 rows x 1 columns]

最后,要知道可以同时按多个列排序:

  1. import pandas as pd
  2. reviews = pd.read_csv("../data/winemag-data-130k-v2.csv", index_col=0)
  3. countries_reviewed = reviews.groupby(['country', 'province']).description.agg([len])
  4. print(countries_reviewed.sort_values(by=['country', 'len']))
  1. len
  2. country province
  3. Argentina Other 536
  4. Mendoza Province 3264
  5. Armenia Armenia 2
  6. Australia Tasmania 42
  7. New South Wales 85
  8. ... ...
  9. Uruguay Montevideo 11
  10. Progreso 11
  11. Juanico 12
  12. Uruguay 24
  13. Canelones 43
  14. [425 rows x 1 columns]