Как разделить фрейм данных Pandas на несколько фреймов данных
Вы можете использовать следующий базовый синтаксис для разделения кадра данных pandas на несколько кадров данных на основе номера строки:
#split DataFrame into two DataFrames at row 6 df1 = df.iloc [:6] df2 = df.iloc [6:]
В следующих примерах показано, как использовать этот синтаксис на практике.
Пример 1: разделить Pandas DataFrame на два DataFrames
Следующий код показывает, как разделить один кадр данных pandas на два кадра данных:
import pandas as pd #create DataFrame df = pd.DataFrame() #view DataFrame df x y 0 1 5 1 1 7 2 1 7 3 3 9 4 3 12 5 4 9 6 5 9 7 5 4 8 5 3 9 6 3 10 7 1 11 9 10 #split original DataFrame into two DataFrames df1 = df.iloc [:6] df2 = df.iloc [6:] #view resulting DataFrames print(df1) x y 0 1 5 1 1 7 2 1 7 3 3 9 4 3 12 5 4 9 print(df2) x y 6 5 9 7 5 4 8 5 3 9 6 3 10 7 1 11 9 10
Обратите внимание, что df1 содержит первые шесть строк исходного DataFrame, а df2 содержит последние шесть строк исходного DataFrame.
Пример 2: Разделение Pandas DataFrame на несколько DataFrames
Следующий код показывает, как разделить pandas
import pandas as pd #create DataFrame df = pd.DataFrame() #split into three DataFrames df1 = df.iloc [:3] df2 = df.iloc [3:6] df3 = df.iloc [6:] #view resulting DataFrames print(df1) x y 0 1 5 1 1 7 2 1 7 print(df2) x y 3 3 9 4 3 12 5 4 9 print(df3) x y 6 5 9 7 5 4 8 5 3 9 6 3 10 7 1 11 9 10
В этом примере мы решили разделить один DataFrame на три DataFrames, но, используя этот синтаксис, мы можем разделить DataFrame pandas на любое количество DataFrames, которое нам нужно.
Дополнительные ресурсы
В следующих руководствах объясняется, как выполнять другие распространенные функции в pandas:
Сгруппировать по: разделить-применить-объединить
Под «группировкой по» мы имеем в виду процесс, включающий один или несколько следующих шагов:
- Разделение данных на группы на основе некоторых критериев.
- Применение функции к каждой группе независимо.
- Объединение результатов в структуру данных.
Из них сплит-шаг является самым простым. На самом деле, во многих ситуациях мы можем захотеть разбить набор данных на группы и что-то сделать с этими группами. На этапе применения мы можем захотеть сделать одно из следующего:
-
Агрегация: вычислить сводную статистику (или статистику) для каждой группы. Некоторые примеры:
- Вычислить групповые суммы или средние значения.
- Вычислите размеры / количество групп.
- Стандартизируйте данные (zscore) внутри группы.
- Заполнение NAs внутри групп значением, полученным из каждой группы.
- Отбрасывать данные, принадлежащие группам с небольшим количеством участников.
- Отфильтруйте данные на основе групповой суммы или среднего значения.
Поскольку набор методов экземпляра объекта в структурах данных pandas, как правило, богатый и выразительный, мы часто хотим просто вызвать, скажем, функцию DataFrame для каждой группы. Имя GroupBy должно быть хорошо знакомо тем, кто использовал инструмент SQL-based (или itertools ), в котором вы можете написать такой код:
SELECT Column1, Column2, mean(Column3), sum(Column4) FROM SomeTable GROUP BY Column1, Column2
Мы стремимся сделать подобные операции естественными и простыми для выражения с помощью pandas. Мы рассмотрим каждую область функциональности GroupBy, а затем предоставим несколько нетривиальных примеров/вариантов использования.
См. cookbook для некоторых продвинутых стратегий.
Разделение объекта на группы
Объекты pandas можно разделить по любой из их осей. Абстрактное определение группировки состоит в том, чтобы обеспечить сопоставление меток с именами групп. Чтобы создать объект GroupBy (подробнее о том, что представляет собой объект GroupBy later),, вы можете сделать следующее:
In [1]: df = pd.DataFrame( . [ . ("bird", "Falconiformes", 389.0), . ("bird", "Psittaciformes", 24.0), . ("mammal", "Carnivora", 80.2), . ("mammal", "Primates", np.nan), . ("mammal", "Carnivora", 58), . ], . index=["falcon", "parrot", "lion", "monkey", "leopard"], . columns=("class", "order", "max_speed"), . ) . In [2]: df Out[2]: class order max_speed falcon bird Falconiformes 389.0 parrot bird Psittaciformes 24.0 lion mammal Carnivora 80.2 monkey mammal Primates NaN leopard mammal Carnivora 58.0 # default is axis=0 In [3]: grouped = df.groupby("class") In [4]: grouped = df.groupby("order", axis="columns") In [5]: grouped = df.groupby(["class", "order"])
Отображение может быть задано разными способами:
- Функция Python, вызываемая для каждой из меток оси.
- Список или массив NumPy той же длины, что и выбранная ось.
- A dict или Series , обеспечивающий сопоставление label -> group name .
- Для объектов DataFrame — строка, указывающая либо имя столбца, либо имя уровня индекса, которое будет использоваться для группировки.
- df.groupby(‘A’) — это просто синтаксический сахар для df.groupby(df[‘A’]) .
- Список любой из вышеперечисленных вещей.
В совокупности мы называем объекты группировки ключами. Например, рассмотрим следующий DataFrame :
Строка, переданная groupby , может относиться либо к столбцу, либо к уровню индекса. Если строка совпадает как с именем столбца, так и с именем уровня индекса, будет поднят вопрос ValueError .
In [6]: df = pd.DataFrame( . < . "A": ["foo", "bar", "foo", "bar", "foo", "bar", "foo", "foo"], . "B": ["one", "one", "two", "three", "two", "two", "one", "three"], . "C": np.random.randn(8), . "D": np.random.randn(8), . > . ) . In [7]: df Out[7]: A B C D 0 foo one 0.469112 -0.861849 1 bar one -0.282863 -2.104569 2 foo two -1.509059 -0.494929 3 bar three -1.135632 1.071804 4 foo two 1.212112 0.721555 5 bar two -0.173215 -0.706771 6 foo one 0.119209 -1.039575 7 foo three -1.044236 0.271860
На DataFrame, мы получаем объект GroupBy, вызывая groupby() . Естественно, мы могли бы сгруппировать либо по столбцам A , либо по B , либо по обоим:
In [8]: grouped = df.groupby("A") In [9]: grouped = df.groupby(["A", "B"])
Если у нас также есть MultiIndex в столбцах A и B , мы можем сгруппировать по всем столбцам, кроме указанных.
In [10]: df2 = df.set_index(["A", "B"]) In [11]: grouped = df2.groupby(level=df2.index.names.difference(["B"])) In [12]: grouped.sum() Out[12]: C D A bar -1.591710 -1.739537 foo -0.752861 -1.402938
Они разделят DataFrame на его индекс (rows).. Мы также можем разделить по столбцам:
In [13]: def get_letter_type(letter): . if letter.lower() in 'aeiou': . return 'vowel' . else: . return 'consonant' . In [14]: grouped = df.groupby(get_letter_type, axis=1)
Объекты pandas Index поддерживают повторяющиеся значения. Если в качестве группового ключа в операции groupby используется неуникальный индекс, все значения для одного и того же значения индекса будут считаться принадлежащими одной группе, и поэтому выходные данные функций агрегации будут содержать только уникальные значения индекса:
In [15]: lst = [1, 2, 3, 1, 2, 3] In [16]: s = pd.Series([1, 2, 3, 10, 20, 30], lst) In [17]: grouped = s.groupby(level=0) In [18]: grouped.first() Out[18]: 1 1 2 2 3 3 dtype: int64 In [19]: grouped.last() Out[19]: 1 10 2 20 3 30 dtype: int64 In [20]: grouped.sum() Out[20]: 1 11 2 22 3 33 dtype: int64
Обратите внимание, что разбиение не происходит до тех пор, пока оно не понадобится. Создание объекта GroupBy только подтверждает, что вы передали действительное сопоставление.
Многие виды сложных манипуляций с данными могут быть выражены в терминах операций GroupBy (хотя нельзя гарантировать, что они будут самыми большими).
GroupBy sorting
По умолчанию групповые ключи сортируются во время операции groupby . Однако вы можете передать sort=False для потенциального ускорения:
In [21]: df2 = pd.DataFrame("B", "B", "A", "A"], "Y": [1, 2, 3, 4]>) In [22]: df2.groupby(["X"]).sum() Out[22]: Y X A 7 B 3 In [23]: df2.groupby(["X"], sort=False).sum() Out[23]: Y X B 3 A 7
Обратите внимание, что groupby сохранит порядок сортировки наблюдений в каждой группе. Например, группы, созданные groupby() ниже, расположены в том порядке, в котором они появились в исходном DataFrame :
In [24]: df3 = pd.DataFrame("A", "B", "A", "B"], "Y": [1, 4, 3, 2]>) In [25]: df3.groupby(["X"]).get_group("A") Out[25]: X Y 0 A 1 2 A 3 In [26]: df3.groupby(["X"]).get_group("B") Out[26]: X Y 1 B 4 3 B 2
Новое в версии 1.1.0.
GroupBy dropna
По умолчанию значения NA исключаются из групповых ключей во время операции groupby . Однако, если вы хотите включить значения NA в групповые ключи, вы можете передать dropna=False для этого.
In [27]: df_list = [[1, 2, 3], [1, None, 4], [2, 1, 3], [1, 2, 2]] In [28]: df_dropna = pd.DataFrame(df_list, columns=["a", "b", "c"]) In [29]: df_dropna Out[29]: a b c 0 1 2.0 3 1 1 NaN 4 2 2 1.0 3 3 1 2.0 2
# Default ``dropna`` is set to True, which will exclude NaNs in keys In [30]: df_dropna.groupby(by=["b"], dropna=True).sum() Out[30]: a c b 1.0 2 3 2.0 2 5 # In order to allow NaN in keys, set ``dropna`` to False In [31]: df_dropna.groupby(by=["b"], dropna=False).sum() Out[31]: a c b 1.0 2 3 2.0 2 5 NaN 1 4
Аргумент dropna по умолчанию имеет значение True , что означает, что NA не включены в групповые ключи.
Атрибуты объекта GroupBy
Атрибут groups — это словарь, ключи которого представляют собой вычисленные уникальные группы, а соответствующие значения — метки осей, принадлежащие каждой группе. В приведенном выше примере мы имеем:
In [32]: df.groupby("A").groups Out[32]: 'bar': [1, 3, 5], 'foo': [0, 2, 4, 6, 7]> In [33]: df.groupby(get_letter_type, axis=1).groups Out[33]: 'consonant': ['B', 'C', 'D'], 'vowel': ['A']>
Вызов стандартной функции Python len для объекта GroupBy просто возвращает длину словаря groups , так что в основном это просто удобство:
In [34]: grouped = df.groupby(["A", "B"]) In [35]: grouped.groups Out[35]: <('bar', 'one'): [1], ('bar', 'three'): [3], ('bar', 'two'): [5], ('foo', 'one'): [0, 6], ('foo', 'three'): [7], ('foo', 'two'): [2, 4]> In [36]: len(grouped) Out[36]: 6
GroupBy будет отображать полные имена столбцов с вкладками (и другие attributes):
In [37]: df Out[37]: height weight gender 2000-01-01 42.849980 157.500553 male 2000-01-02 49.607315 177.340407 male 2000-01-03 56.293531 171.524640 male 2000-01-04 48.421077 144.251986 female 2000-01-05 46.556882 152.526206 male 2000-01-06 68.448851 168.272968 female 2000-01-07 70.757698 136.431469 male 2000-01-08 58.909500 176.499753 female 2000-01-09 76.435631 174.094104 female 2000-01-10 45.306120 177.540920 male In [38]: gb = df.groupby("gender")
In [39]: gb. # номер: E225, E999 gb.agg gb.boxplot gb.cummin gb.describe gb.filter gb.get_group gb.height gb.last gb.median gb.ngroups gb.plot gb.rank gb.std gb.transform gb.aggregate gb.count gb.cumprod gb.dtype gb.first gb.groups gb.hist gb.max gb.min gb.nth gb.prod gb.resample gb.sum gb.var gb.apply gb.cummax gb.cumsum gb.fillna gb.gender gb.head gb.indices gb.mean gb.name gb.ohlc gb.quantile gb.size gb.tail gb.weight
GroupBy с MultiIndex
С hierarchically-indexed data вполне естественно группировать по одному из уровней иерархии.
Создадим Series с двухуровневым MultiIndex .
In [40]: arrays = [ . ["bar", "bar", "baz", "baz", "foo", "foo", "qux", "qux"], . ["one", "two", "one", "two", "one", "two", "one", "two"], . ] . In [41]: index = pd.MultiIndex.from_arrays(arrays, names=["first", "second"]) In [42]: s = pd.Series(np.random.randn(8), index=index) In [43]: s Out[43]: first second bar one -0.919854 two -0.042379 baz one 1.247642 two -0.009920 foo one 0.290213 two 0.495767 qux one 0.362949 two 1.548106 dtype: float64
Затем мы можем сгруппировать по одному из уровней в s .
In [44]: grouped = s.groupby(level=0) In [45]: grouped.sum() Out[45]: first bar -0.962232 baz 1.237723 foo 0.785980 qux 1.911055 dtype: float64
Если для MultiIndex указаны имена, их можно передать вместо номера уровня:
In [46]: s.groupby(level="second").sum() Out[46]: second one 0.980950 two 1.991575 dtype: float64
Поддерживается группировка с несколькими уровнями.
In [47]: s Out[47]: first second third bar doo one -1.131345 two -0.089329 baz bee one 0.337863 two -0.945867 foo bop one -0.932132 two 1.956030 qux bop one 0.017587 two -0.016692 dtype: float64 In [48]: s.groupby(level=["first", "second"]).sum() Out[48]: first second bar doo -1.220674 baz bee -0.608004 foo bop 1.023898 qux bop 0.000895 dtype: float64
Имена уровней индекса могут быть предоставлены как ключи.
In [49]: s.groupby(["first", "second"]).sum() Out[49]: first second bar doo -1.220674 baz bee -0.608004 foo bop 1.023898 qux bop 0.000895 dtype: float64
Подробнее о функции sum и агрегации позже.
Группировка DataFrame с уровнями индекса и столбцами
DataFrame можно сгруппировать по комбинации столбцов и уровней индексов, указав имена столбцов в виде строк и уровни индексов в виде объектов pd.Grouper .
In [50]: arrays = [ . ["bar", "bar", "baz", "baz", "foo", "foo", "qux", "qux"], . ["one", "two", "one", "two", "one", "two", "one", "two"], . ] . In [51]: index = pd.MultiIndex.from_arrays(arrays, names=["first", "second"]) In [52]: df = pd.DataFrame("A": [1, 1, 1, 1, 2, 2, 3, 3], "B": np.arange(8)>, index=index) In [53]: df Out[53]: A B first second bar one 1 0 two 1 1 baz one 1 2 two 1 3 foo one 2 4 two 2 5 qux one 3 6 two 3 7
В следующем примере df группируется по уровню индекса second и столбцу A .
In [54]: df.groupby([pd.Grouper(level=1), "A"]).sum() Out[54]: B second A one 1 2 2 4 3 6 two 1 4 2 5 3 7
Уровни индекса также могут быть указаны по имени.
In [55]: df.groupby([pd.Grouper(level="second"), "A"]).sum() Out[55]: B second A one 1 2 2 4 3 6 two 1 4 2 5 3 7
Имена уровней индекса могут быть указаны как ключи непосредственно к groupby .
In [56]: df.groupby(["second", "A"]).sum() Out[56]: B second A one 1 2 2 4 3 6 two 1 4 2 5 3 7
Выбор столбца DataFrame в GroupBy
После того, как вы создали объект GroupBy из DataFrame,, вы можете захотеть сделать что-то другое для каждого из столбцов. Таким образом, используя [] аналогично получению столбца из DataFrame,, вы можете сделать:
In [57]: df = pd.DataFrame( . < . "A": ["foo", "bar", "foo", "bar", "foo", "bar", "foo", "foo"], . "B": ["one", "one", "two", "three", "two", "two", "one", "three"], . "C": np.random.randn(8), . "D": np.random.randn(8), . > . ) . In [58]: df Out[58]: A B C D 0 foo one -0.575247 1.346061 1 bar one 0.254161 1.511763 2 foo two -1.143704 1.627081 3 bar three 0.215897 -0.990582 4 foo two 1.193555 -0.441652 5 bar two -0.077118 1.211526 6 foo one -0.408530 0.268520 7 foo three -0.862495 0.024580 In [59]: grouped = df.groupby(["A"]) In [60]: grouped_C = grouped["C"] In [61]: grouped_D = grouped["D"]
Это в основном синтаксический сахар для альтернативы и гораздо более подробный:
In [62]: df[«C»].groupby(df[«A»]) Out[62]:
Кроме того, этот метод позволяет избежать повторного вычисления внутренней информации о группировке, полученной из переданного ключа.
Итерация по группам
С объектом GroupBy перебор сгруппированных данных очень естественен и работает аналогично itertools.groupby() :
In [63]: grouped = df.groupby('A') In [64]: for name, group in grouped: . print(name) . print(group) . bar A B C D 1 bar one 0.254161 1.511763 3 bar three 0.215897 -0.990582 5 bar two -0.077118 1.211526 foo A B C D 0 foo one -0.575247 1.346061 2 foo two -1.143704 1.627081 4 foo two 1.193555 -0.441652 6 foo one -0.408530 0.268520 7 foo three -0.862495 0.024580
В случае группировки по нескольким ключам именем группы будет кортеж:
In [65]: for name, group in df.groupby(['A', 'B']): . print(name) . print(group) . ('bar', 'one') A B C D 1 bar one 0.254161 1.511763 ('bar', 'three') A B C D 3 bar three 0.215897 -0.990582 ('bar', 'two') A B C D 5 bar two -0.077118 1.211526 ('foo', 'one') A B C D 0 foo one -0.575247 1.346061 6 foo one -0.408530 0.268520 ('foo', 'three') A B C D 7 foo three -0.862495 0.02458 ('foo', 'two') A B C D 2 foo two -1.143704 1.627081 4 foo two 1.193555 -0.441652
Выбор группы
Одну группу можно выбрать с помощью get_group() :
In [66]: grouped.get_group("bar") Out[66]: A B C D 1 bar one 0.254161 1.511763 3 bar three 0.215897 -0.990582 5 bar two -0.077118 1.211526
Или для объекта, сгруппированного по нескольким столбцам:
In [67]: df.groupby(["A", "B"]).get_group(("bar", "one")) Out[67]: A B C D 1 bar one 0.254161 1.511763
Aggregation
После создания объекта GroupBy становится доступным несколько методов для выполнения вычислений по сгруппированным данным. Эти операции аналогичны aggregating API , window API и resample API .
Очевидным является агрегирование с помощью метода aggregate() или эквивалентного метода agg() :
In [68]: grouped = df.groupby("A") In [69]: grouped[["C", "D"]].aggregate(np.sum) Out[69]: C D A bar 0.392940 1.732707 foo -1.796421 2.824590 In [70]: grouped = df.groupby(["A", "B"]) In [71]: grouped.aggregate(np.sum) Out[71]: C D A B bar one 0.254161 1.511763 three 0.215897 -0.990582 two -0.077118 1.211526 foo one -0.983776 1.614581 three -0.862495 0.024580 two 0.049851 1.185429
Как видите, результат агрегации будет иметь имена групп в качестве нового индекса вдоль сгруппированной оси. В случае нескольких ключей результатом по умолчанию является MultiIndex , хотя это можно изменить с помощью параметра as_index :
In [72]: grouped = df.groupby(["A", "B"], as_index=False) In [73]: grouped.aggregate(np.sum) Out[73]: A B C D 0 bar one 0.254161 1.511763 1 bar three 0.215897 -0.990582 2 bar two -0.077118 1.211526 3 foo one -0.983776 1.614581 4 foo three -0.862495 0.024580 5 foo two 0.049851 1.185429 In [74]: df.groupby("A", as_index=False)[["C", "D"]].sum() Out[74]: A C D 0 bar 0.392940 1.732707 1 foo -1.796421 2.824590
Обратите внимание, что вы можете использовать функцию reset_index DataFrame для достижения того же результата, поскольку имена столбцов хранятся в результирующем MultiIndex :
In [75]: df.groupby(["A", "B"]).sum().reset_index() Out[75]: A B C D 0 bar one 0.254161 1.511763 1 bar three 0.215897 -0.990582 2 bar two -0.077118 1.211526 3 foo one -0.983776 1.614581 4 foo three -0.862495 0.024580 5 foo two 0.049851 1.185429
Другой простой пример агрегирования — вычисление размера каждой группы. Это включено в GroupBy как метод size . Он возвращает серию, индексом которой являются имена групп, а значениями — размеры каждой группы.
In [76]: grouped.size() Out[76]: A B size 0 bar one 1 1 bar three 1 2 bar two 1 3 foo one 2 4 foo three 1 5 foo two 2
In [77]: grouped.describe() Out[77]: C . D count mean std min . 25% 50% 75% max 0 1.0 0.254161 NaN 0.254161 . 1.511763 1.511763 1.511763 1.511763 1 1.0 0.215897 NaN 0.215897 . -0.990582 -0.990582 -0.990582 -0.990582 2 1.0 -0.077118 NaN -0.077118 . 1.211526 1.211526 1.211526 1.211526 3 2.0 -0.491888 0.117887 -0.575247 . 0.537905 0.807291 1.076676 1.346061 4 1.0 -0.862495 NaN -0.862495 . 0.024580 0.024580 0.024580 0.024580 5 2.0 0.024925 1.652692 -1.143704 . 0.075531 0.592714 1.109898 1.627081 [6 rows x 16 columns]
Другой пример агрегирования — вычисление количества уникальных значений каждой группы. Это похоже на функцию value_counts , за исключением того, что она подсчитывает только уникальные значения.
In [78]: ll = [['foo', 1], ['foo', 2], ['foo', 2], ['bar', 1], ['bar', 1]] In [79]: df4 = pd.DataFrame(ll, columns=["A", "B"]) In [80]: df4 Out[80]: A B 0 foo 1 1 foo 2 2 foo 2 3 bar 1 4 bar 1 In [81]: df4.groupby("A")["B"].nunique() Out[81]: A bar 1 foo 2 Name: B, dtype: int64
Функции агрегирования не будут возвращать группы, по которым выполняется агрегирование, если они являются именованными столбцами, когда as_index=True используется по умолчанию. Сгруппированные столбцы будут индексами возвращаемого объекта.
Передача as_index=False вернет группы, по которым выполняется агрегирование, если они являются именованными столбцами.
Агрегирующие функции — это функции, которые уменьшают размерность возвращаемых объектов. Некоторые общие функции агрегирования представлены в таблице ниже:
Вычислить среднее значение групп
Вычислить сумму групповых значений
Расчет размеров групп
Вычислить количество группы
Стандартное отклонение групп
Вычислить дисперсию групп
Стандартная ошибка среднего значения групп
Генерирует описательную статистику
Вычислить первое из групповых значений
Вычислить последнее значение группы
Возьмите n-е значение или подмножество, если n — список
Вычислить минимум групповых значений
Вычислить максимальное количество значений группы
Приведенные выше функции агрегирования будут исключать значения NA. Любая функция, которая сводит Series к скалярному значению, является функцией агрегирования и будет работать, тривиальный пример — df.groupby(‘A’).agg(lambda ser: 1) . Обратите внимание, что nth() может действовать как редуктор или фильтр, см. here .
Применение нескольких функций одновременно
С помощью сгруппированного Series вы также можете передать список или список функций для агрегирования, выводя DataFrame:
In [82]: grouped = df.groupby("A") In [83]: grouped["C"].agg([np.sum, np.mean, np.std]) Out[83]: sum mean std A bar 0.392940 0.130980 0.181231 foo -1.796421 -0.359284 0.912265
В сгруппированном DataFrame вы можете передать список функций для применения к каждому столбцу, что даст агрегированный результат с иерархическим индексом:
In [84]: grouped[["C", "D"]].agg([np.sum, np.mean, np.std]) Out[84]: C D sum mean std sum mean std A bar 0.392940 0.130980 0.181231 1.732707 0.577569 1.366330 foo -1.796421 -0.359284 0.912265 2.824590 0.564918 0.884785
Результирующие агрегаты названы в честь самих функций. Если вам нужно переименовать, вы можете добавить цепочку операций для Series следующим образом:
In [85]: ( . grouped["C"] . .agg([np.sum, np.mean, np.std]) . .rename(columns="sum": "foo", "mean": "bar", "std": "baz">) . ) . Out[85]: foo bar baz A bar 0.392940 0.130980 0.181231 foo -1.796421 -0.359284 0.912265
Для сгруппированного DataFrame вы можете переименовать аналогичным образом:
In [86]: ( . grouped[["C", "D"]].agg([np.sum, np.mean, np.std]).rename( . columns="sum": "foo", "mean": "bar", "std": "baz"> . ) . ) . Out[86]: C D foo bar baz foo bar baz A bar 0.392940 0.130980 0.181231 1.732707 0.577569 1.366330 foo -1.796421 -0.359284 0.912265 2.824590 0.564918 0.884785
Как правило, имена выходных столбцов должны быть уникальными. Вы не можете применить одну и ту же функцию (или две функции с одинаковым именем) к одному и тому же столбцу.
In [87]: grouped["C"].agg(["sum", "sum"]) Out[87]: sum sum A bar 0.392940 0.392940 foo -1.796421 -1.796421
pandas позволяет вам предоставлять несколько lambda. В этом случае pandas изменит имя (безымянной) функции lambda, добавляя _ к каждой последующей функции lambda.
In [88]: grouped["C"].agg([lambda x: x.max() - x.min(), lambda x: x.median() - x.mean()]) Out[88]: A bar 0.331279 0.084917 foo 2.337259 -0.215962
Named aggregation
Новое в версии 0.25.0.
Для поддержки специфичной для столбца агрегации с контролем имен выходных столбцов pandas принимает специальный синтаксис в GroupBy.agg() , известный как «именованная агрегация», где
- Ключевые слова — это имена выходных столбцов.
- Значения — это кортежи, первый элемент которых — столбец для выбора, а второй элемент — агрегация, применяемая к этому столбцу. pandas предоставляет именованный кортеж pandas.NamedAgg с полями [‘column’, ‘aggfunc’] , чтобы было понятнее, что представляют собой аргументы. Как обычно, агрегация может быть вызываемым или строковым псевдонимом.
In [89]: animals = pd.DataFrame( . < . "kind": ["cat", "dog", "cat", "dog"], . "height": [9.1, 6.0, 9.5, 34.0], . "weight": [7.9, 7.5, 9.9, 198.0], . > . ) . In [90]: animals Out[90]: kind height weight 0 cat 9.1 7.9 1 dog 6.0 7.5 2 cat 9.5 9.9 3 dog 34.0 198.0 In [91]: animals.groupby("kind").agg( . min_height=pd.NamedAgg(column="height", aggfunc="min"), . max_height=pd.NamedAgg(column="height", aggfunc="max"), . average_weight=pd.NamedAgg(column="weight", aggfunc=np.mean), . ) . Out[91]: min_height max_height average_weight kind cat 9.1 9.5 8.90 dog 6.0 34.0 102.75
pandas.NamedAgg — это всего лишь namedtuple . Также разрешены простые кортежи.
In [92]: animals.groupby("kind").agg( . min_height=("height", "min"), . max_height=("height", "max"), . average_weight=("weight", np.mean), . ) . Out[92]: min_height max_height average_weight kind cat 9.1 9.5 8.90 dog 6.0 34.0 102.75
Если желаемые имена выходных столбцов не являются допустимыми ключевыми словами Python, создайте словарь и распакуйте аргументы ключевых слов.
In [93]: animals.groupby("kind").agg( . **< . "total weight": pd.NamedAgg(column="weight", aggfunc=sum) . > . ) . Out[93]: total weight kind cat 17.8 dog 205.5
Дополнительные аргументы ключевого слова не передаются функциям агрегации. Только пары (column, aggfunc) должны передаваться как **kwargs . Если ваши функции агрегации требуют дополнительных аргументов, частично примените их с помощью functools.partial() .
Для Python 3.5 и ранее порядок **kwargs в функциях не сохранялся. Это означает, что порядок выходных столбцов не будет последовательным. Для обеспечения последовательного порядка ключи (и, следовательно, выходные столбцы) всегда будут сортироваться для Python 3.5.
Именованная агрегация также действительна для агрегаций Series groupby. В этом случае нет выбора столбца, поэтому значения — это просто функции.
In [94]: animals.groupby("kind").height.agg( . min_height="min", . max_height="max", . ) . Out[94]: min_height max_height kind cat 9.1 9.5 dog 6.0 34.0
Применение различных функций к столбцам DataFrame
Передав dict в aggregate , вы можете применить другую агрегацию к столбцам DataFrame:.
In [95]: grouped.agg("D": lambda x: np.std(x, ddof=1)>) Out[95]: C D A bar 0.392940 1.366330 foo -1.796421 0.884785
Имена функций также могут быть строками. Чтобы строка была действительной, она должна быть либо реализована на GroupBy, либо доступна через dispatching :
In [96]: grouped.agg("sum", "D": "std">) Out[96]: C D A bar 0.392940 1.366330 foo -1.796421 0.884785
Функции агрегации, оптимизированные для Cython
Некоторые распространенные агрегаты, в настоящее время только sum , mean , std и sem , имеют оптимизированные реализации Cython:
In [97]: df.groupby("A")[["C", "D"]].sum() Out[97]: C D A bar 0.392940 1.732707 foo -1.796421 2.824590 In [98]: df.groupby(["A", "B"]).mean() Out[98]: C D A B bar one 0.254161 1.511763 three 0.215897 -0.990582 two -0.077118 1.211526 foo one -0.491888 0.807291 three -0.862495 0.024580 two 0.024925 0.592714
Разумеется, sum и mean реализованы на объектах pandas, поэтому приведенный выше код будет работать даже без специальных версий через диспетчеризацию (см. below).
Агрегации с функциями User-Defined
Пользователи также могут предоставлять свои собственные функции для пользовательских агрегаций. При объединении с функцией User-Defined (UDF), UDF не должен изменять предоставленный Series , см. Mutating with User Defined Function (UDF) methods для получения дополнительной информации.
In [99]: animals.groupby("kind")[["height"]].agg(lambda x: set(x)) Out[99]: height kind cat 9.1, 9.5> dog 34.0, 6.0>
Полученный dtype будет отражать тип агрегатной функции. Если результаты из разных групп имеют разные dtype, то общий dtype будет определяться так же, как построение DataFrame .
In [100]: animals.groupby("kind")[["height"]].agg(lambda x: x.astype(int).sum()) Out[100]: height kind cat 18 dog 40
Transformation
Метод transform возвращает объект, индексированный так же, как и группируемый. Функция преобразования должна:
- Возвращает результат, который либо имеет тот же размер, что и групповой фрагмент, либо транслируется до размера группового фрагмента (e.g., (скаляр, grouped.transform(lambda x: x.iloc[-1]) ).
- Работайте столбец за столбцом в групповом фрагменте. Преобразование применяется к первому фрагменту группы с использованием chunk.apply..
- Не выполнять операции на месте над фрагментом группы. Групповые фрагменты следует рассматривать как неизменяемые, и изменения в групповом фрагменте могут привести к неожиданным результатам. Например, при использовании fillna inplace должен быть False ( grouped.transform(lambda x: x.fillna(inplace=False)) ).
- (Optionally) работает со всем фрагментом группы. Если это поддерживается, используется быстрый путь, начиная со второго фрагмента.
Устарело, начиная с версии 1.5.0: При использовании .transform на сгруппированном DataFrame и функции преобразования возвращает DataFrame, в настоящее время pandas не выравнивает индекс результата с индексом ввода. Это поведение устарело, и выравнивание будет выполняться в будущей версии pandas. Вы можете применить .to_numpy() к результату функции преобразования, чтобы избежать выравнивания.
Подобно Aggregations with User-Defined Functions , результирующий dtype будет отражать тип функции преобразования. Если результаты из разных групп имеют разные dtype, то общий dtype будет определяться так же, как построение DataFrame .
Предположим, мы хотели стандартизировать данные внутри каждой группы:
In [101]: index = pd.date_range("10/1/1999", periods=1100) In [102]: ts = pd.Series(np.random.normal(0.5, 2, 1100), index) In [103]: ts = ts.rolling(window=100, min_periods=100).mean().dropna() In [104]: ts.head() Out[104]: 2000-01-08 0.779333 2000-01-09 0.778852 2000-01-10 0.786476 2000-01-11 0.782797 2000-01-12 0.798110 Freq: D, dtype: float64 In [105]: ts.tail() Out[105]: 2002-09-30 0.660294 2002-10-01 0.631095 2002-10-02 0.673601 2002-10-03 0.709213 2002-10-04 0.719369 Freq: D, dtype: float64 In [106]: transformed = ts.groupby(lambda x: x.year).transform( . lambda x: (x - x.mean()) / x.std() . ) .
Мы ожидаем, что теперь результат будет иметь среднее значение 0 и стандартное отклонение 1 в каждой группе, что мы можем легко проверить:
# Исходные данные In [107]: grouped = ts.groupby(lambda x: x.year) In [108]: grouped.mean() Out[108]: 2000 0.442441 2001 0.526246 2002 0.459365 dtype: float64 In [109]: grouped.std() Out[109]: 2000 0.131752 2001 0.210945 2002 0.128753 dtype: float64 # Преобразованные данные In [110]: grouped_trans = transformed.groupby(lambda x: x.year) In [111]: grouped_trans.mean() Out[111]: 2000 -4.870756e-16 2001 -1.545187e-16 2002 4.136282e-16 dtype: float64 In [112]: grouped_trans.std() Out[112]: 2000 1.0 2001 1.0 2002 1.0 dtype: float64
Мы также можем визуально сравнить исходные и преобразованные наборы данных.
In [113]: compare = pd.DataFrame(«Transformed»: transformed>) In [114]: compare.plot() Out[114]:
Функции преобразования, которые имеют выходные данные меньшего размера, транслируются в соответствии с формой входного массива.
In [115]: ts.groupby(lambda x: x.year).transform(lambda x: x.max() - x.min()) Out[115]: 2000-01-08 0.623893 2000-01-09 0.623893 2000-01-10 0.623893 2000-01-11 0.623893 2000-01-12 0.623893 . 2002-09-30 0.558275 2002-10-01 0.558275 2002-10-02 0.558275 2002-10-03 0.558275 2002-10-04 0.558275 Freq: D, Length: 1001, dtype: float64
В качестве альтернативы можно использовать встроенные методы для получения тех же результатов.
In [116]: max_ts = ts.groupby(lambda x: x.year).transform("max") In [117]: min_ts = ts.groupby(lambda x: x.year).transform("min") In [118]: max_ts - min_ts Out[118]: 2000-01-08 0.623893 2000-01-09 0.623893 2000-01-10 0.623893 2000-01-11 0.623893 2000-01-12 0.623893 . 2002-09-30 0.558275 2002-10-01 0.558275 2002-10-02 0.558275 2002-10-03 0.558275 2002-10-04 0.558275 Freq: D, Length: 1001, dtype: float64
Другим распространенным преобразованием данных является замена отсутствующих данных групповым средним значением.
In [119]: data_df Out[119]: A B C 0 1.539708 -1.166480 0.533026 1 1.302092 -0.505754 NaN 2 -0.371983 1.104803 -0.651520 3 -1.309622 1.118697 -1.161657 4 -1.924296 0.396437 0.812436 .. . . . 995 -0.093110 0.683847 -0.774753 996 -0.185043 1.438572 NaN 997 -0.394469 -0.642343 0.011374 998 -1.174126 1.857148 NaN 999 0.234564 0.517098 0.393534 [1000 rows x 3 columns] In [120]: countries = np.array(["US", "UK", "GR", "JP"]) In [121]: key = countries[np.random.randint(0, 4, 1000)] In [122]: grouped = data_df.groupby(key) # Non-NA count in each group In [123]: grouped.count() Out[123]: A B C GR 209 217 189 JP 240 255 217 UK 216 231 193 US 239 250 217 In [124]: transformed = grouped.transform(lambda x: x.fillna(x.mean()))
Мы можем убедиться, что групповые средние не изменились в преобразованных данных и что преобразованные данные не содержат NAs..
In [125]: grouped_trans = transformed.groupby(key) In [126]: grouped.mean() # исходная группа означает Out[126]: A B C GR -0.098371 -0.015420 0.068053 JP 0.069025 0.023100 -0.077324 UK 0.034069 -0.052580 -0.116525 US 0.058664 -0.020399 0.028603 In [127]: grouped_trans.mean() # преобразование не изменило группу средств Out[127]: A B C GR -0.098371 -0.015420 0.068053 JP 0.069025 0.023100 -0.077324 UK 0.034069 -0.052580 -0.116525 US 0.058664 -0.020399 0.028603 In [128]: grouped.count() # в оригинале отсутствуют некоторые точки данных Out[128]: A B C GR 209 217 189 JP 240 255 217 UK 216 231 193 US 239 250 217 In [129]: grouped_trans.count() # считает после преобразования Out[129]: A B C GR 228 228 228 JP 267 267 267 UK 247 247 247 US 258 258 258 In [130]: grouped_trans.size() # Убедитесь, что количество non-NA равно размеру группы Out[130]: GR 228 JP 267 UK 247 US 258 dtype: int64
Некоторые функции автоматически преобразуют ввод при применении к объекту GroupBy, но возвращают объект той же формы, что и оригинал. Прохождение as_index=False не повлияет на эти методы преобразования.
Например: fillna, ffill, bfill, shift. .
In [131]: grouped.ffill() Out[131]: A B C 0 1.539708 -1.166480 0.533026 1 1.302092 -0.505754 0.533026 2 -0.371983 1.104803 -0.651520 3 -1.309622 1.118697 -1.161657 4 -1.924296 0.396437 0.812436 .. . . . 995 -0.093110 0.683847 -0.774753 996 -0.185043 1.438572 -0.774753 997 -0.394469 -0.642343 0.011374 998 -1.174126 1.857148 -0.774753 999 0.234564 0.517098 0.393534 [1000 rows x 3 columns]
Операции с окном и передискретизацией
Можно использовать resample() , expanding() и rolling() как методы на groupbys.
В приведенном ниже примере будет применен метод rolling() к образцам столбца B на основе групп столбца A.
In [132]: df_re = pd.DataFrame(A": [1] * 10 + [5] * 10, "B": np.arange(20)>) In [133]: df_re Out[133]: A B 0 1 0 1 1 1 2 1 2 3 1 3 4 1 4 .. .. .. 15 5 15 16 5 16 17 5 17 18 5 18 19 5 19 [20 rows x 2 columns] In [134]: df_re.groupby("A").rolling(4).B.mean() Out[134]: A 1 0 NaN 1 NaN 2 NaN 3 1.5 4 2.5 . 5 15 13.5 16 14.5 17 15.5 18 16.5 19 17.5 Name: B, Length: 20, dtype: float64
Метод expanding() будет накапливать заданную операцию ( sum() в примере) для всех членов каждой конкретной группы.
In [135]: df_re.groupby("A").expanding().sum() Out[135]: B A 1 0 0.0 1 1.0 2 3.0 3 6.0 4 10.0 . . 5 15 75.0 16 91.0 17 108.0 18 126.0 19 145.0 [20 rows x 1 columns]
Предположим, вы хотите использовать метод resample() для получения ежедневной частоты в каждой группе вашего фрейма данных и хотите дополнить недостающие значения методом ffill() .
In [136]: df_re = pd.DataFrame( . < . "date": pd.date_range(start="2016-01-01", periods=4, freq="W"), . "group": [1, 1, 2, 2], . "val": [5, 6, 7, 8], . > . ).set_index("date") . In [137]: df_re Out[137]: group val date 2016-01-03 1 5 2016-01-10 1 6 2016-01-17 2 7 2016-01-24 2 8 In [138]: df_re.groupby("group").resample("1D").ffill() Out[138]: group val group date 1 2016-01-03 1 5 2016-01-04 1 5 2016-01-05 1 5 2016-01-06 1 5 2016-01-07 1 5 . . . 2 2016-01-20 2 7 2016-01-21 2 7 2016-01-22 2 7 2016-01-23 2 7 2016-01-24 2 8 [16 rows x 2 columns]
Filtration
Метод filter возвращает подмножество исходного объекта. Предположим, мы хотим взять только элементы, принадлежащие группам с групповой суммой больше 2.
In [139]: sf = pd.Series([1, 1, 2, 3, 3, 3]) In [140]: sf.groupby(sf).filter(lambda x: x.sum() > 2) Out[140]: 3 3 4 3 5 3 dtype: int64
Аргумент filter должен быть функцией, которая применительно к группе в целом возвращает True или False .
Еще одна полезная операция — отфильтровать элементы, принадлежащие группам, состоящим всего из пары членов.
In [141]: dff = pd.DataFrame(A": np.arange(8), "B": list("aabbbbcc")>) In [142]: dff.groupby("B").filter(lambda x: len(x) > 2) Out[142]: A B 2 2 b 3 3 b 4 4 b 5 5 b
В качестве альтернативы, вместо того, чтобы отбрасывать группы-нарушители, мы можем вернуть объекты с похожим индексом, где группы, не прошедшие фильтр, заполняются NaNs..
In [143]: dff.groupby("B").filter(lambda x: len(x) > 2, dropna=False) Out[143]: A B 0 NaN NaN 1 NaN NaN 2 2.0 b 3 3.0 b 4 4.0 b 5 5.0 b 6 NaN NaN 7 NaN NaN
Для DataFrames с несколькими столбцами фильтры должны явно указывать столбец в качестве критерия фильтра.
In [144]: dff["C"] = np.arange(8) In [145]: dff.groupby("B").filter(lambda x: len(x["C"]) > 2) Out[145]: A B C 2 2 b 2 3 3 b 3 4 4 b 4 5 5 b 5
Некоторые функции при применении к объекту groupby будут действовать как фильтр на входе, возвращая уменьшенную форму оригинала (и потенциально устраняя groups),, но с неизменным индексом. Передача as_index=False не повлияет на эти методы преобразования.
Например: head, tail .
In [146]: dff.groupby("B").head(2) Out[146]: A B C 0 0 a 0 1 1 a 1 2 2 b 2 3 3 b 3 6 6 c 6 7 7 c 7
Отправка на методы экземпляра
При выполнении агрегации или преобразования вы можете просто вызвать метод экземпляра для каждой группы данных. Это довольно легко сделать, передав функции lambda:
In [147]: grouped = df.groupby("A") In [148]: grouped.agg(lambda x: x.std()) Out[148]: C D A bar 0.181231 1.366330 foo 0.912265 0.884785
Но это довольно многословно и может быть неопрятным, если вам нужно передать дополнительные аргументы. Используя немного хитрости метапрограммирования, GroupBy теперь имеет возможность «отправлять» вызовы методов в группы:
In [149]: grouped.std() Out[149]: C D A bar 0.181231 1.366330 foo 0.912265 0.884785
На самом деле здесь происходит то, что создается оболочка функции. При вызове он принимает любые переданные аргументы и вызывает функцию с любыми аргументами для каждой группы (в приведенном выше примере результаты std function). затем объединяются вместе во многом в стиле agg и transform (на самом деле он использует apply для вывода склейки, задокументированного next).). Это позволяет выполнять некоторые операции довольно лаконично:
In [150]: tsdf = pd.DataFrame( . np.random.randn(1000, 3), . index=pd.date_range("1/1/2000", periods=1000), . columns=["A", "B", "C"], . ) . In [151]: tsdf.iloc[::2] = np.nan In [152]: grouped = tsdf.groupby(lambda x: x.year) In [153]: grouped.fillna(method="pad") Out[153]: A B C 2000-01-01 NaN NaN NaN 2000-01-02 -0.353501 -0.080957 -0.876864 2000-01-03 -0.353501 -0.080957 -0.876864 2000-01-04 0.050976 0.044273 -0.559849 2000-01-05 0.050976 0.044273 -0.559849 . . . . 2002-09-22 0.005011 0.053897 -1.026922 2002-09-23 0.005011 0.053897 -1.026922 2002-09-24 -0.456542 -1.849051 1.559856 2002-09-25 -0.456542 -1.849051 1.559856 2002-09-26 1.123162 0.354660 1.128135 [1000 rows x 3 columns]
В этом примере мы разбили набор временных рядов на годовые фрагменты, а затем независимо назвали fillna для групп.
Методы nlargest и nsmallest работают с группами в стиле Series :
In [154]: s = pd.Series([9, 8, 7, 5, 19, 1, 4.2, 3.3]) In [155]: g = pd.Series(list("abababab")) In [156]: gb = s.groupby(g) In [157]: gb.nlargest(3) Out[157]: a 4 19.0 0 9.0 2 7.0 b 1 8.0 3 5.0 7 3.3 dtype: float64 In [158]: gb.nsmallest(3) Out[158]: a 6 4.2 2 7.0 0 9.0 b 5 1.0 7 3.3 3 5.0 dtype: float64
Flexible apply
Некоторые операции над сгруппированными данными могут не подпадать ни под категории агрегирования, ни под категории преобразования. Или вы можете просто захотеть, чтобы GroupBy сделал вывод, как объединить результаты. Для них используйте функцию apply , которая во многих стандартных случаях может заменить как aggregate , так и transform . Однако apply может обрабатывать некоторые исключительные случаи использования.
apply может выступать в качестве редуктора, преобразователя или функции фильтра, в зависимости от того, что ему передается. Это может зависеть от переданной функции и того, что именно вы группируете. Таким образом, сгруппированный column(s) может быть включен в вывод, а также для установки индексов.
In [159]: df Out[159]: A B C D 0 foo one -0.575247 1.346061 1 bar one 0.254161 1.511763 2 foo two -1.143704 1.627081 3 bar three 0.215897 -0.990582 4 foo two 1.193555 -0.441652 5 bar two -0.077118 1.211526 6 foo one -0.408530 0.268520 7 foo three -0.862495 0.024580 In [160]: grouped = df.groupby("A") # could also just call .describe() In [161]: grouped["C"].apply(lambda x: x.describe()) Out[161]: A bar count 3.000000 mean 0.130980 std 0.181231 min -0.077118 25% 0.069390 . foo min -1.143704 25% -0.862495 50% -0.575247 75% -0.408530 max 1.193555 Name: C, Length: 16, dtype: float64
Размер возвращаемого результата также может измениться:
In [162]: grouped = df.groupby('A')['C'] In [163]: def f(group): . return pd.DataFrame('original': group, . 'demeaned': group - group.mean()>) .
apply в серии может работать с возвращаемым значением прикладной функции, которая сама является серией, и, возможно, преобразовать результат в DataFrame:.
In [164]: def f(x): . return pd.Series([x, x ** 2], index=["x", "x^2"]) . In [165]: s = pd.Series(np.random.rand(5)) In [166]: s Out[166]: 0 0.321438 1 0.493496 2 0.139505 3 0.910103 4 0.194158 dtype: float64 In [167]: s.apply(f) Out[167]: x x^2 0 0.321438 0.103323 1 0.493496 0.243538 2 0.139505 0.019462 3 0.910103 0.828287 4 0.194158 0.037697
Контрольное сгруппированное размещение column(s) с group_keys
Если при вызове groupby() указано group_keys=True , функции, переданные в apply , которые возвращают выходные данные с аналогичным индексом, будут иметь групповые ключи, добавленные к индексу результата. Предыдущие версии pandas добавляли групповые ключи только тогда, когда результат примененной функции имел индекс, отличный от входного. Если group_keys не указан, групповые ключи не будут добавлены для выходов с одинаковым индексом. В будущем это поведение изменится, чтобы всегда учитывать group_keys , который по умолчанию True .
Изменено в версии 1.5.0.
Чтобы контролировать, включены ли сгруппированные column(s) в индексы, вы можете использовать аргумент group_keys . Сравнивать
In [168]: df.groupby("A", group_keys=True).apply(lambda x: x) Out[168]: A B C D A bar 1 bar one 0.254161 1.511763 3 bar three 0.215897 -0.990582 5 bar two -0.077118 1.211526 foo 0 foo one -0.575247 1.346061 2 foo two -1.143704 1.627081 4 foo two 1.193555 -0.441652 6 foo one -0.408530 0.268520 7 foo three -0.862495 0.024580
In [169]: df.groupby("A", group_keys=False).apply(lambda x: x) Out[169]: A B C D 0 foo one -0.575247 1.346061 1 bar one 0.254161 1.511763 2 foo two -1.143704 1.627081 3 bar three 0.215897 -0.990582 4 foo two 1.193555 -0.441652 5 bar two -0.077118 1.211526 6 foo one -0.408530 0.268520 7 foo three -0.862495 0.024580
Подобно Aggregations with User-Defined Functions , результирующий dtype будет отражать тип функции применения. Если результаты из разных групп имеют разные dtype, то общий dtype будет определяться так же, как построение DataFrame .
Ускоренные процедуры Numba
Новое в версии 1.1.
Если Numba установлен в качестве дополнительной зависимости, методы transform и aggregate поддерживают аргументы engine=’numba’ и engine_kwargs . См. enhancing performance with Numba для общего использования аргументов и соображений производительности.
Сигнатура функции должна начинаться с values, index точно так же, как данные, принадлежащие каждой группе, будут переданы в values , а индекс группы будет передан в index .
При использовании engine=’numba’ не будет внутреннего «отката». Данные группы и индекс группы будут переданы в виде массивов NumPy в пользовательскую функцию JITed, и никакие альтернативные попытки выполнения предприниматься не будут.
Другие полезные функции
Автоматическое исключение «неприятных» столбцов
Снова рассмотрим пример DataFrame, который мы рассматривали:
In [170]: df Out[170]: A B C D 0 foo one -0.575247 1.346061 1 bar one 0.254161 1.511763 2 foo two -1.143704 1.627081 3 bar three 0.215897 -0.990582 4 foo two 1.193555 -0.441652 5 bar two -0.077118 1.211526 6 foo one -0.408530 0.268520 7 foo three -0.862495 0.024580
Предположим, мы хотим вычислить стандартное отклонение, сгруппированное по столбцу A . Есть небольшая проблема, а именно то, что нас не интересуют данные в столбце B . Мы называем это «неприятной» колонкой. Вы можете избежать ненужных столбцов, указав numeric_only=True :
In [171]: df.groupby("A").std(numeric_only=True) Out[171]: C D A bar 0.181231 1.366330 foo 0.912265 0.884785
Обратите внимание, что df.groupby(‘A’).colname.std(). более эффективен, чем df.groupby(‘A’).std().colname , поэтому, если результат функции агрегирования интересен только для одного столбца (здесь colname ), его можно отфильтровать перед применением функции агрегации.
Любой столбец объекта, даже если он содержит числовые значения, такие как объекты Decimal , рассматривается как «мешающий» столбец. Они автоматически исключаются из агрегатных функций в groupby.
Если вы хотите включить десятичные или объектные столбцы в агрегацию с другими типами данных, не вызывающими неудобств, вы должны сделать это явно.
Автоматическое удаление нежелательных столбцов устарело и будет удалено в будущей версии pandas. Если включены столбцы, с которыми нельзя работать, pandas вместо этого вызовет ошибку. Чтобы избежать этого, либо выберите столбцы, с которыми вы хотите работать, либо укажите numeric_only=True .
In [172]: from decimal import Decimal In [173]: df_dec = pd.DataFrame( . < . "id": [1, 2, 1, 2], . "int_column": [1, 2, 3, 4], . "dec_column": [ . Decimal("0.50"), . Decimal("0.15"), . Decimal("0.25"), . Decimal("0.40"), . ], . > . ) . # Десятичные столбцы можно явно суммировать с помощью themselves. In [174]: df_dec.groupby(["id"])[["dec_column"]].sum() Out[174]: dec_column id 1 0.75 2 0.55 # . but нельзя комбинировать со стандартными типами данных, иначе они будут исключены In [175]: df_dec.groupby(["id"])[["int_column", "dec_column"]].sum() Out[175]: int_column id 1 4 2 6 # Используйте функцию .agg для агрегирования стандартных и "неудобных" типов данных # в то же время In [176]: df_dec.groupby(["id"]).agg("int_column": "sum", "dec_column": "sum">) Out[176]: int_column dec_column id 1 4 0.75 2 6 0.55
Обработка категориальных значений (un)observed
При использовании группировщика Categorical (как одного группировщика или как части нескольких groupers), ключевое слово observed определяет, следует ли возвращать декартово произведение всех возможных значений группировщиков ( observed=False ) или только тех, которые наблюдаются группировщики ( observed=True ).
Показать все значения:
In [177]: pd.Series([1, 1, 1]).groupby( . pd.Categorical(["a", "a", "a"], categories=["a", "b"]), observed=False . ).count() . Out[177]: a 3 b 0 dtype: int64
Показать только наблюдаемые значения:
In [178]: pd.Series([1, 1, 1]).groupby( . pd.Categorical(["a", "a", "a"], categories=["a", "b"]), observed=True . ).count() . Out[178]: a 3 dtype: int64
Возвращаемый тип группы всегда будет включать все категории, которые были сгруппированы.
In [179]: s = ( . pd.Series([1, 1, 1]) . .groupby(pd.Categorical(["a", "a", "a"], categories=["a", "b"]), observed=False) . .count() . ) . In [180]: s.index.dtype Out[180]: CategoricalDtype(categories=['a', 'b'], ordered=False)
Обработка групп NA и NaT
Если в ключе группировки есть какие-либо значения NaN или NaT, они будут автоматически исключены. Другими словами, никогда не будет группы “NA» или группы “NaT». В старых версиях pandas такого не было, но пользователи, как правило, все равно отказывались от группы NA (а ее поддержка была реализацией headache).).
Группировка с упорядоченными факторами
Категориальные переменные, представленные как экземпляр класса pandas Categorical , могут использоваться в качестве групповых ключей. Если да, то порядок уровней сохранится:
In [181]: data = pd.Series(np.random.randn(100)) In [182]: factor = pd.qcut(data, [0, 0.25, 0.5, 0.75, 1.0]) In [183]: data.groupby(factor).mean() Out[183]: (-2.645, -0.523] -1.362896 (-0.523, 0.0296] -0.260266 (0.0296, 0.654] 0.361802 (0.654, 2.21] 1.073801 dtype: float64
Группировка со спецификацией группировщика
Возможно, вам потребуется указать немного больше данных для правильной группировки. Вы можете использовать pd.Grouper для обеспечения этого местного управления.
In [184]: import datetime In [185]: df = pd.DataFrame( . < . "Branch": "A A A A A A A B".split(), . "Buyer": "Carl Mark Carl Carl Joe Joe Joe Carl".split(), . "Quantity": [1, 3, 5, 1, 8, 1, 9, 3], . "Date": [ . datetime.datetime(2013, 1, 1, 13, 0), . datetime.datetime(2013, 1, 1, 13, 5), . datetime.datetime(2013, 10, 1, 20, 0), . datetime.datetime(2013, 10, 2, 10, 0), . datetime.datetime(2013, 10, 1, 20, 0), . datetime.datetime(2013, 10, 2, 10, 0), . datetime.datetime(2013, 12, 2, 12, 0), . datetime.datetime(2013, 12, 2, 14, 0), . ], . > . ) . In [186]: df Out[186]: Branch Buyer Quantity Date 0 A Carl 1 2013-01-01 13:00:00 1 A Mark 3 2013-01-01 13:05:00 2 A Carl 5 2013-10-01 20:00:00 3 A Carl 1 2013-10-02 10:00:00 4 A Joe 8 2013-10-01 20:00:00 5 A Joe 1 2013-10-02 10:00:00 6 A Joe 9 2013-12-02 12:00:00 7 B Carl 3 2013-12-02 14:00:00
Сгруппировать по определенному столбцу с нужной периодичностью. Это похоже на повторную выборку.
In [187]: df.groupby([pd.Grouper(freq="1M", key="Date"), "Buyer"])[["Quantity"]].sum() Out[187]: Quantity Date Buyer 2013-01-31 Carl 1 Mark 3 2013-10-31 Carl 6 Joe 9 2013-12-31 Carl 3 Joe 9
У вас неоднозначная спецификация, поскольку у вас есть именованный индекс и столбец, которые могут быть потенциальными группировщиками.
In [188]: df = df.set_index("Date") In [189]: df["Date"] = df.index + pd.offsets.MonthEnd(2) In [190]: df.groupby([pd.Grouper(freq="6M", key="Date"), "Buyer"])[["Quantity"]].sum() Out[190]: Quantity Date Buyer 2013-02-28 Carl 1 Mark 3 2014-02-28 Carl 9 Joe 18 In [191]: df.groupby([pd.Grouper(freq="6M", level="Date"), "Buyer"])[["Quantity"]].sum() Out[191]: Quantity Date Buyer 2013-01-31 Carl 1 Mark 3 2014-01-31 Carl 9 Joe 18
Берем первые ряды каждой группы
Так же, как и для DataFrame или Series, вы можете вызвать голову и хвост для группы:
In [192]: df = pd.DataFrame([[1, 2], [1, 4], [5, 6]], columns=["A", "B"]) In [193]: df Out[193]: A B 0 1 2 1 1 4 2 5 6 In [194]: g = df.groupby("A") In [195]: g.head(1) Out[195]: A B 0 1 2 2 5 6 In [196]: g.tail(1) Out[196]: A B 1 1 4 2 5 6
Это показывает первые или последние n строк из каждой группы.
Берем n-й ряд каждой группы
Чтобы выбрать из DataFrame или Series n-й элемент, используйте nth() . Это метод сокращения, и он вернет одну строку (или ни одной строки) для каждой группы, если вы передадите int для n:
In [197]: df = pd.DataFrame([[1, np.nan], [1, 4], [5, 6]], columns=["A", "B"]) In [198]: g = df.groupby("A") In [199]: g.nth(0) Out[199]: B A 1 NaN 5 6.0 In [200]: g.nth(-1) Out[200]: B A 1 4.0 5 6.0 In [201]: g.nth(1) Out[201]: B A 1 4.0
Если вы хотите выбрать n-й элемент не-null, используйте kwarg dropna . Для DataFrame это должно быть либо ‘any’ , либо ‘all’ , как если бы вы перешли к dropna:
# nth(0) is the same as g.first() In [202]: g.nth(0, dropna="any") Out[202]: B A 1 4.0 5 6.0 In [203]: g.first() Out[203]: B A 1 4.0 5 6.0 # nth(-1) is the same as g.last() In [204]: g.nth(-1, dropna="any") # NaNs denote group exhausted when using dropna Out[204]: B A 1 4.0 5 6.0 In [205]: g.last() Out[205]: B A 1 4.0 5 6.0 In [206]: g.B.nth(0, dropna="all") Out[206]: A 1 4.0 5 6.0 Name: B, dtype: float64
Как и в случае с другими методами, при передаче as_index=False достигается фильтрация, которая возвращает сгруппированную строку.
In [207]: df = pd.DataFrame([[1, np.nan], [1, 4], [5, 6]], columns=["A", "B"]) In [208]: g = df.groupby("A", as_index=False) In [209]: g.nth(0) Out[209]: A B 0 1 NaN 2 5 6.0 In [210]: g.nth(-1) Out[210]: A B 1 1 4.0 2 5 6.0
Вы также можете выбрать несколько строк из каждой группы, указав несколько значений n в виде списка целых чисел.
In [211]: business_dates = pd.date_range(start="4/1/2014", end="6/30/2014", freq="B") In [212]: df = pd.DataFrame(1, index=business_dates, columns=["a", "b"]) # get the first, 4th, and last date index for each month In [213]: df.groupby([df.index.year, df.index.month]).nth([0, 3, -1]) Out[213]: a b 2014 4 1 1 4 1 1 4 1 1 5 1 1 5 1 1 5 1 1 6 1 1 6 1 1 6 1 1
Перечислить элементы группы
Чтобы увидеть порядок, в котором каждая строка появляется в своей группе, используйте метод cumcount :
In [214]: dfg = pd.DataFrame(list("aaabba"), columns=["A"]) In [215]: dfg Out[215]: A 0 a 1 a 2 a 3 b 4 b 5 a In [216]: dfg.groupby("A").cumcount() Out[216]: 0 0 1 1 2 2 3 0 4 1 5 3 dtype: int64 In [217]: dfg.groupby("A").cumcount(ascending=False) Out[217]: 0 3 1 2 2 1 3 1 4 0 5 0 dtype: int64
Enumerate groups
Чтобы увидеть порядок групп (в отличие от порядка строк внутри группы, заданного cumcount ), вы можете использовать ngroup() .
Обратите внимание, что числа, присвоенные группам, соответствуют порядку, в котором группы будут видны при итерации по объекту groupby, а не порядку, в котором они впервые наблюдаются.
In [218]: dfg = pd.DataFrame(list("aaabba"), columns=["A"]) In [219]: dfg Out[219]: A 0 a 1 a 2 a 3 b 4 b 5 a In [220]: dfg.groupby("A").ngroup() Out[220]: 0 0 1 0 2 0 3 1 4 1 5 0 dtype: int64 In [221]: dfg.groupby("A").ngroup(ascending=False) Out[221]: 0 1 1 1 2 1 3 0 4 0 5 1 dtype: int64
Plotting
Groupby также работает с некоторыми методами построения графиков. Например, предположим, что мы подозреваем, что некоторые функции в DataFrame могут различаться по группам, в этом случае значения в столбце 1, где группа — “B”, в среднем на 3 выше.
In [222]: np.random.seed(1234) In [223]: df = pd.DataFrame(np.random.randn(50, 2)) In [224]: df["g"] = np.random.choice(["A", "B"], size=50) In [225]: df.loc[df["g"] == "B", 1] += 3
Мы можем легко визуализировать это с помощью boxplot:
In [226]: df.groupby("g").boxplot() Out[226]: A AxesSubplot(0.1,0.15;0.363636x0.75) B AxesSubplot(0.536364,0.15;0.363636x0.75) dtype: object
Результатом вызова boxplot является словарь, ключами которого являются значения нашего группирующего столбца g , (“A” и “B”).. Значения результирующего словаря можно контролировать с помощью ключевого слова return_type boxplot . См. visualization documentation для получения дополнительной информации.
По историческим причинам df.groupby(«g»).boxplot() не эквивалентен df.boxplot(by=»g») . Объяснение см. в here .
Вызовы функций конвейера
Подобно функциям, предоставляемым DataFrame и Series , функции, которые принимают объекты GroupBy , могут быть объединены в цепочку с помощью метода pipe , чтобы обеспечить более чистый и читаемый синтаксис. Общие сведения о .pipe см. в here .
Сочетание .groupby и .pipe часто полезно, когда вам нужно повторно использовать объекты GroupBy.
В качестве примера представьте, что у вас есть DataFrame со столбцами для магазинов, продуктов, доходов и проданного количества. Мы хотели бы сделать групповой расчет цен (i.e. revenue/quantity) для каждого магазина и для каждого продукта. Мы могли бы сделать это в виде многоэтапной операции, но выражение в виде конвейера может сделать код более читаемым. Сначала устанавливаем данные:
In [227]: n = 1000 In [228]: df = pd.DataFrame( . < . "Store": np.random.choice(["Store_1", "Store_2"], n), . "Product": np.random.choice(["Product_1", "Product_2"], n), . "Revenue": (np.random.random(n) * 50 + 10).round(2), . "Quantity": np.random.randint(1, 10, size=n), . > . ) . In [229]: df.head(2) Out[229]: Store Product Revenue Quantity 0 Store_2 Product_1 26.12 1 1 Store_2 Product_1 28.86 1
Теперь, чтобы найти цены на store/product,, мы можем просто сделать:
In [230]: ( . df.groupby(["Store", "Product"]) . .pipe(lambda grp: grp.Revenue.sum() / grp.Quantity.sum()) . .unstack() . .round(2) . ) . Out[230]: Product Product_1 Product_2 Store Store_1 6.82 7.05 Store_2 6.30 6.64
Конвейер также может быть выразительным, когда вы хотите передать сгруппированный объект какой-либо произвольной функции, например:
In [231]: def mean(groupby): . return groupby.mean() . In [232]: df.groupby(["Store", "Product"]).pipe(mean) Out[232]: Revenue Quantity Store Product Store_1 Product_1 34.622727 5.075758 Product_2 35.482815 5.029630 Store_2 Product_1 32.972837 5.237589 Product_2 34.684360 5.224000
где mean берет объект GroupBy и находит среднее значение столбцов Revenue и Quantity соответственно для каждой комбинации Store-Product. Функция mean может быть любой функцией, принимающей объект GroupBy; .pipe передаст объект GroupBy в качестве параметра в указанную вами функцию.
Examples
Перегруппировка по фактору
Перегруппируйте столбцы DataFrame в соответствии с их суммой и просуммируйте агрегированные столбцы.
In [233]: df = pd.DataFrame(a": [1, 0, 0], "b": [0, 1, 0], "c": [1, 0, 0], "d": [2, 3, 4]>) In [234]: df Out[234]: a b c d 0 1 0 1 2 1 0 1 0 3 2 0 0 0 4 In [235]: df.groupby(df.sum(), axis=1).sum() Out[235]: 1 9 0 2 2 1 1 3 2 0 4
Multi-column factorization
Используя ngroup() , мы можем извлекать информацию о группах способом, аналогичным factorize() (как описано далее в reshaping API ), но который естественным образом применяется к нескольким столбцам смешанного типа и из разных источников. Это может быть полезно в качестве промежуточного этапа обработки, подобного категориальному, когда отношения между строками группы важнее их содержимого, или в качестве входных данных для алгоритма, который принимает только целочисленное кодирование. (For дополнительную информацию о поддержке в pandas для полных категорийных данных см. в Categorical introduction и API documentation .)
In [236]: dfg = pd.DataFrame(A": [1, 1, 2, 3, 2], "B": list("aaaba")>) In [237]: dfg Out[237]: A B 0 1 a 1 1 a 2 2 a 3 3 b 4 2 a In [238]: dfg.groupby(["A", "B"]).ngroup() Out[238]: 0 0 1 0 2 1 3 2 4 1 dtype: int64 In [239]: dfg.groupby(["A", [0, 0, 0, 1, 1]]).ngroup() Out[239]: 0 0 1 0 2 1 3 3 4 2 dtype: int64
Сгруппировать по индексатору для «пересчета» данных
Повторная выборка создает новые гипотетические выборки (повторные выборки) из уже существующих наблюдаемых данных или из модели, которая генерирует данные. Эти новые образцы аналогичны ранее существовавшим образцам.
Для повторной выборки для работы с индексами, не подобными дате и времени, можно использовать следующую процедуру.
В следующих примерах df.index // 5 возвращает двоичный массив, который используется для определения того, что будет выбрано для операции groupby.
В приведенном ниже примере показано, как мы можем понизить выборку путем объединения выборок в меньшее количество выборок. Здесь, используя df.index // 5, мы агрегируем выборки в ячейки. Применяя функцию std(), мы объединяем информацию, содержащуюся во многих выборках, в небольшое подмножество значений, которое является их стандартным отклонением, тем самым уменьшая количество выборок.
In [240]: df = pd.DataFrame(np.random.randn(10, 2)) In [241]: df Out[241]: 0 1 0 -0.793893 0.321153 1 0.342250 1.618906 2 -0.975807 1.918201 3 -0.810847 -1.405919 4 -1.977759 0.461659 5 0.730057 -1.316938 6 -0.751328 0.528290 7 -0.257759 -1.081009 8 0.505895 -1.701948 9 -1.006349 0.020208 In [242]: df.index // 5 Out[242]: Int64Index([0, 0, 0, 0, 0, 1, 1, 1, 1, 1], dtype='int64') In [243]: df.groupby(df.index // 5).std() Out[243]: 0 1 0 0.823647 1.312912 1 0.760109 0.942941
Возврат серии для распространения имен
Группируйте столбцы DataFrame, вычисляйте набор показателей и возвращайте именованный ряд. Имя серии используется в качестве имени индекса столбца. Это особенно полезно в сочетании с операциями изменения формы, такими как стекирование, в котором имя индекса столбца будет использоваться как имя вставляемого столбца:
In [244]: df = pd.DataFrame( . < . "a": [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2], . "b": [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], . "c": [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], . "d": [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], . > . ) . In [245]: def compute_metrics(x): . result = "b"].sum(), "c_mean": x["c"].mean()> . return pd.Series(result, name="metrics") . In [246]: result = df.groupby("a").apply(compute_metrics) In [247]: result Out[247]: metrics b_sum c_mean a 0 2.0 0.5 1 2.0 0.5 2 2.0 0.5 In [248]: result.stack() Out[248]: a metrics 0 b_sum 2.0 c_mean 0.5 1 b_sum 2.0 c_mean 0.5 2 b_sum 2.0 c_mean 0.5 dtype: float64
Разбить датафрейм на несколько частей по условию
Количество значений в датафрейме может быть любое, но гарантировано, что есть как минимум 2 повторяющиеся части и одна лишняя строка, которая их разделяет (значение этой строки тоже может быть произвольным). Как разделить такой датафрейм на несколько частей, при этом чтоб во втором датафрейме повторялись названия столбцов как в первом Пример для использования
q = [['a', 'b', 'c'], ['количество', 23, 45], ['итог', 100, 120], ['лишнее', 'лишнее', 'лишнее'], ['количество', 35, 87], ['итог', 76, 90]] qw = pd.DataFrame(q, columns=q[0]) qw = qw[1:] qw
Нужно получить два датафрейма
[['a', 'b', 'c'], ['количество', 23, 45], ['итог', 100, 120]]
[['a', 'b', 'c'], ['количество', 35, 87], ['итог', 76, 90]]
7 полезных операций в Pandas при работе с DataFrame
Абстракция датафрейма является одной из наиболее полезных концепций в современной экосистеме управления данными. Вращается она главным образом вокруг табличных структур, которые имеют повышенную производительность при обновлении и запросе данных различными способами. Сериализация/десериализация этих структур из/в различные форматы файлов упрощает работу с данными. Более того, возможность производить различные SQL-подобные операции, такие как объединение, наряду с выполнением математических вычислений в самом датафрейме существенно расширяет возможности программиста.
Эта статья подчеркивает некоторые наиболее полезные операции, которые можно выполнять с помощью абстракции датафрейма. Реализовывать мы их будем через библиотеку Pandas. Постараюсь представить материал в интуитивно понятной форме, чтобы в дальнейшем вы могли применить эти знания в других случаях или при работе с другими фреймворками.
1. Конкатенация DataFrame
Есть два способа конкатенировать датафрейм A и B . Представьте их как проиндексированные таблицы. Эти две таблицы можно объединить либо по оси y, либо по оси x.
Если требуется конкатенировать их вдоль x, то вызов API будет таким:
import pandas as pdpd.concat([A, B], axis=1)
Если же вдоль y, то таким:
import pandas as pdpd.concat([A, B]) # по умолчанию axis = 0
Применение
Предположим, у вас есть большое количество CSV-файлов или XLSX-данных, которые нужно присоединить друг к другу. Одним из способов сделать это будет считать данные файлов в датафреймы и использовать инструкцию pd.concat([file_1, file_2]) . Программно можно перебрать имена файлов (используя модуль glob для чтения набора имен файлов с помощью техники сопоставления шаблонов, например regex), считать их в датафрейм, соединить в памяти и сериализовать конкатенированные датафреймы в нужный формат файлов.
Вариант с axis = 0 используется нечасто, но его можно применять в сценариях, когда нужно обработать массивы данных, собранных с упорядочиванием. То есть, когда последовательность данных соответствует последовательности других массивов данных. В таком случае эти массивы можно объединить вдоль оси x, получив более объемное и значительное представление в табличном формате. Затем к полученной структуре можно применять операции, использующие все типы данных в ее столбцах.
2. Разделение DataFrame
Датафрейм можно разделить множеством способов, и выбор техники полностью зависит от цели этого разделения. Рассмотрим ряд случаев.
Просмотр сведений
В некоторых сценариях, особенно при написании кода с помощью блокнотов (например Jupyter), мы заглядываем в датафрейм, только чтобы понять, как он выглядит. В таких случаях можно использовать метод head() .
import pandas as pdprint(df.head(10)) # выводит первые 10 строк dataframe
Исключение столбцов
Этот метод разделяет датафрейм вдоль оси y, то есть просто выбрасывает из него часть столбцов. Используется данный метод в типичном сценарии, когда нам не нужно, чтобы конечный DataFrame содержал эти столбцы, или когда мы предполагаем, что при дальнейшем обновлении структура станет занимать слишком много памяти.
import pandas as pddf.drop('COLUMN_NAME', inplace=True, axis=1)
# указывает, что 'COLUMN_NAME' находится на оси x
Удаление датафреймов друг из друга
Представим, что у нас есть датафрейм X , состоящий из столбцов [A, B, C, D] , и датафрейм Y , состоящий из подмножества столбцов X . Нам нужно удалить Y из X . Это можно сделать так:
import pandas as pdpd.concat([X, Y]).drop_duplicates(keep=False)
Конкатенация этих датафреймов приведет к дублированию общих записей, которые в итоге будут удалены выражением keep = false функции drop_duplicates() .
Применение
Предположим, что столбец A — это определенный вид ID сведений о работнике. К примеру, датафрейм X состоит из всех данных о работниках, а датафрейм Y содержит данные (с той же структурой) о работниках, не разбирающихся в Python. Нам нужно отфильтровать сведения о сотрудниках, которые не знакомы с Python.
Определение дельты записей на основе столбца
Представим, что у нас есть датафрейм X , состоящий из столбцов [A, B, C, D] , а также датафрейм Y , состоящий из тех же столбцов. При этом некоторые элементы столбцов A этих датафреймов являются общими. Нам нужно получить из датафрейма X строки, которые не содержат значения из столбца A , находящиеся в столбце A датафрейма Y .
import pandas as pdX[~X['A'].isin(Y['A'])]
Применение
Взгляните на эту таблицу:
Эти пары могли быть сгенерированы, например, из двух журналов: старого и нового. Нам нужно найти пары ID, принадлежащие одному и тому же человеку. Предположим, что ваш отдел кадров неожиданно заявляет, что определенный список ( hr_list ) сотрудников с ID_1 больше в компании не работает. Как удалить их из этого датафрейма?
import pandas as pdfiltered_pairs = employee_pairs[~employee_pairs['ID_1'].isin(hr_list)]
Разделение на основе значений столбцов
Датафрейм можно фильтровать на основе значений столбца. В этом случае критерий отбора может включать несколько выражений при условии, что они будут возвращать логические значения.
import pandas as pdfiltered_df = df[~df['A'].isna() & (df['B'] > 10)]
# Возвращает строки, где столбец A не null, а значение столбца B больше 10.
Это простейший пример.
3. Подсчет записей в столбце
Это эффективный способ определения количества различных элементов в столбце.
Ответ на приведенный выше запрос можно получить следующим подходом:
import pandas as pd
df = df[ (1984 df.columns = ['Contry', 'Count'] "nbformat": 4,
"nbformat_minor": 0,
"metadata": "colab": "name": "Host Countries.ipynb",
"provenance": []
>,
"kernelspec": "name": "python3",
"display_name": "Python 3"
>
>,
"cells": [
"cell_type": "code",
"metadata": "id": "502dBbjWHq89"
>,
"source": [
"import pandas as pd\n",
"\n",
"df = pd.read_excel('Asia Cup Winners in Cricket.xlsx')"
],
"execution_count": null,
"outputs": []
>,
"cell_type": "code",
"metadata": "colab": "base_uri": "https://localhost:8080/"
>,
"id": "14WfNdQRIjWw",
"outputId": "2b1e8cc9-75c1-40a5-8d2b-b45ed1641723"
>,
"source": [
"df.shape"
],
"execution_count": null,
"outputs": [
"output_type": "execute_result",
"data": "text/plain": [
"(13, 7)"
]
>,
"metadata": "tags": []
>,
"execution_count": 9
>
]
>,
"cell_type": "code",
"metadata": "id": "eSwxxIeTH90N"
>,
"source": [
"df = df[ (1984 ],
"execution_count": null,
"outputs": []
>,
"cell_type": "code",
"metadata": "colab": "base_uri": "https://localhost:8080/"
>,
"id": "1X6LwBL-IBcB",
"outputId": "9277501d-c02e-4345-a38c-66371d50979d"
>,
"source": [
"df.shape"
],
"execution_count": null,
"outputs": [
"output_type": "execute_result",
"data": "text/plain": [
"(12, 7)"
]
>,
"metadata": "tags": []
>,
"execution_count": 11
>
]
>,
"cell_type": "code",
"metadata": "id": "ZC1nYNy1IaT5"
>,
"source": [
"df = df['Host'].value_counts().reset_index()\n",
"df.columns = ['Country', 'Counts']"
],
"execution_count": null,
"outputs": []
>,
"cell_type": "code",
"metadata": "colab": "base_uri": "https://localhost:8080/",
"height": 204
>,
"id": "PdFw0MMkIwDH",
"outputId": "8daa83cb-af9f-41aa-b3e9-71d6a948de51"
>,
"source": [
"df"
],
"execution_count": null,
"outputs": [
"output_type": "execute_result",
"data": "text/html": [
"\n","
"\n",
"\n",
\n",
"\n", \n",
" \n",
"Country \n",
"Counts \n",
"
" \n",
" \n",
"\n", \n",
"0 \n",
"Sri Lanka \n",
"4 \n",
"
"\n", \n",
"1 \n",
"Bangladesh \n",
"4 \n",
"
"\n", \n",
"2 \n",
"UAE \n",
"2 \n",
"
"\n", \n",
"3 \n",
"Pakistan \n",
"1 \n",
"
"\n", \n",
"4 \n",
"India \n",
"1 \n",
"
" \n",
"
"
],
"text/plain": [
" Country Counts\n",
"0 Sri Lanka 4\n",
"1 Bangladesh 4\n",
"2 UAE 2\n",
"3 Pakistan 1\n",
"4 India 1"
]
>,
"metadata": "tags": []
>,
"execution_count": 13
>
]
>
]
>
4. Чтение фрагментов DataFrame
В некоторых случаях будет более эффективно считывать только части датафрейма, особенно при его больших размерах. Обратите внимание, что каждый датафрейм является индексированной табличной структурой, находящейся в памяти, а значит потребляющей пространство, потенциально нужное другим структурам данных. В связи с этим при работе с большими массивами информации всегда лучше считывать только ее нужную часть.
import pandas as pdpd.read_csv('file.csv', usecols=['A', 'B'])
# Считать только столбцы 'A' и 'B'
Более того, можно считывать большие файлы в отдельные фрагменты и маршалировать их в датафреймы.
import pandas as pdfor chunk in pd.read_csv('file.csv', chunksize=1000):
process(chunk)
Таким образом одновременно в памяти удерживается только фрагмент размером chunksize .
5. Применение функций к строкам
Бывают случаи, в которых требуется внести изменения в конкретные столбцы детафрейма. К примеру, в датафрейме X , содержащем столбцы A , B и C , мы можем применить функцию f() к значениям столбца B , чтобы сохранить их в столбце D .
import pandas as pddf['D'] = df.apply(lambda row: f(row['B'], row['C']), axis=1)
Эта операция окажется намного быстрее, чем перебор всего датафрейма с помощью iterrows() .
Есть и альтернативный метод. Его можно использовать, когда функцию f() требуется применить только к одному столбцу.
import pandas as pddf['D'] = df['B'].map(lambda b: f(b))
6. Объединение двух датафреймов
По аналогии с реляционными базами данных датафреймы можно объединять merge , используя разрешающий столбец.
import pandas as pdpd.merge(left=df_a, left_on['A'], right=df_b, right_on=['B'], how='inner')
# Также работает для left и right объединения.
Однако стоит заметить, что операция merge является дорогостоящей, в связи с чем перед слиянием больших датасетов стоит проявлять особое внимание. В случаях, когда датасеты слишком велики, рекомендуется использовать методы группировки (англ.), чтобы избежать перегрузки памяти и связанных с этим проблем производительности.
7. Переименование столбцов
Переименовывать столбцы особенно полезно перед сериализацией файла или перед внедрением стороннего хранилища данных.
import pandas
df = df.rename(columns= "A_1": "A"
"B_1": "B"
>)
# переименовывает оригинальные столбцы ["A_1", "B_1"] в ["A", "B"].
- Новая библиотека превосходит Pandas по производительности
- 7 трюков pandas для науки о данных
- Хватит использовать Pandas, пора переходить на Spark + Scala!