Индексирование и выбор данных
Информация о метках осей в объектах pandas служит для многих целей:
- Идентифицирует данные (т. е. предоставляет метаданные) с использованием известных индикаторов, важных для анализа, визуализации и интерактивного отображения в консоли
- Обеспечивает автоматическое и явное выравнивание данных
- Позволяет интуитивно получать и устанавливать подмножества набора данных
В этом разделе мы сосредоточимся на последнем пункте: как нарезать, измельчить и в целом получать и устанавливать подмножества объектов pandas. Основное внимание будет уделено Series и DataFrame, так как они получили больше внимания в этой области. В будущем ожидается больше работы с многомерными структурами данных (включая Panel), особенно в области расширенного индексирования с использованием меток.
Примечание
Операторы индексирования Python и NumPy [] и оператор атрибутов . обеспечивают быстрый и легкий доступ к структурам данных pandas в широком диапазоне случаев использования. Это делает интерактивную работу интуитивно понятной, поскольку практически ничего нового не нужно изучать, если вы уже знаете, как работать со словарями Python и массивами NumPy. Однако, поскольку тип данных, к которому нужно получить доступ, заранее неизвестен, непосредственное использование стандартных операторов имеет некоторые ограничения по оптимизации. Для кода в производстве мы рекомендуем воспользоваться оптимизированными методами доступа к данным pandas, представленными в этой главе.
Предупреждение
Возвращается ли копия или ссылка при операции установки, может зависеть от контекста. Это иногда называется chained assignment и следует избегать. См. Возвращение представления вместо копии
Предупреждение
В версии 0.15.0 Index внутренне был переработан, чтобы больше не наследоваться от ndarray, а вместо этого наследоваться от PandasObject, подобно остальным объектам pandas. Это должно быть прозрачное изменение с очень ограниченными последствиями для API (см. Внутренняя рефакторинг)
Предупреждение
Индексирование на основе целого числа с плавающей точкой было уточнено в версии 0.18.0. Подробнее о изменениях см. здесь.
См. Многоуровневый индекс / Расширенное индексирование для MultiIndex и более подробной документации по расширенному индексированию.
См. справочник для некоторых расширенных стратегий
Различные варианты индексирования
Впервые добавлено в версии 0.11.0.
Выбор объекта был дополнен рядом запросов пользователей, чтобы обеспечить более явное индексирование на основе местоположения. Pandas теперь поддерживает три типа многоосевого индексирования.
-
.locв основном основан на метках, но также может использоваться с булевым массивом..locвызоветKeyError, если элементы не найдены. Допустимые входные данные:- Единая метка, например,
5или'a'(обратите внимание, что5интерпретируется как метка индекса. Это использование не является целочисленной позицией вдоль индекса) - Список или массив меток
['a', 'b', 'c'] - Объект среза с метками
'a':'f'(обратите внимание, что в отличие от обычных срезов Python, оба начала и конца включены!) - Булев массив
-
Функция
callableс одним аргументом (вызываемый Series, DataFrame или Panel), которая возвращает допустимый результат для индексирования (один из вышеперечисленных)Впервые добавлено в версии 0.18.1.
См. подробнее в разделе Выбор по метке
- Единая метка, например,
-
.ilocв основном основан на целочисленной позиции (от0доlength-1оси), но также может использоваться с булевым массивом..ilocвызоветIndexError, если запрашиваемый индексатор находится вне границ, за исключением индексаторов среза, которые позволяют индексировать значения за пределами границ. (это соответствует семантике Python/NumPy среза). Допустимые входные данные:- Целое число, например,
5 - Список или массив целых чисел
[4, 3, 0] - Объект среза с целыми числами
1:7 - Булев массив
-
Функция
callableс одним аргументом (вызываемый Series, DataFrame или Panel), которая возвращает допустимый результат для индексирования (один из вышеперечисленных)Впервые добавлено в версии 0.18.1.
См. подробнее в разделе Выбор по позиции
- Целое число, например,
-
.ixподдерживает смешанный доступ на основе целых чисел и меток. Он в основном основан на метках, но переходит к целочисленному позиционному доступу, если соответствующая ось имеет целочисленный тип..ixявляется наиболее общим и поддерживает любые входные данные в.locи.iloc..ixтакже поддерживает схемы меток с плавающей точкой..ixособенно полезен при работе с иерархическими индексами, основанными на смешанных позиционных и метках.Однако, когда ось основана на целых числах, только доступ на основе меток, а не позиционный доступ, поддерживается. Таким образом, в таких случаях обычно лучше быть явным и использовать
.ilocили.loc.См. подробнее в разделе Расширенное индексирование и Расширенные иерархические.
-
.loc,.iloc,.ixи также[]индексирование могут приниматьcallableв качестве индексатора. Подробнее см. в разделе Выбор по вызываемому методу.
Получение значений из объекта с выбором по нескольким осям использует следующий формат (используя .loc в качестве примера, но применяется также к .iloc и .ix). Любой из осевых аксессоров может быть пустым срезом :. Оси, не указанные в спецификации, предполагаются :. (например, p.loc['a'] эквивалентно p.loc['a', :, :])
| Тип объекта | Индексаторы |
|---|---|
| Series | s.loc[indexer] |
| DataFrame | df.loc[row_indexer,column_indexer] |
| Panel | p.loc[item_indexer,major_indexer,minor_indexer] |
Основы
Как упоминалось при представлении структур данных в предыдущем разделе, основная функция индексирования с помощью [] (также известная как __getitem__ для тех, кто знаком с реализацией поведения класса в Python) заключается в выделении двумерных срезов. Таким образом,
| Тип объекта | Выбор | Тип возвращаемого значения |
|---|---|---|
| Series | series[label] | скалярное значение |
| DataFrame | frame[colname] |
Series соответствующий столбцу |
| Panel | panel[itemname] |
DataFrame соответствующий имени элемента |
Здесь мы создаем простой временной ряд данных, чтобы проиллюстрировать функциональность индексирования:
In [1]: dates = pd.date_range('1/1/2000', periods=8)
In [2]: df = pd.DataFrame(np.random.randn(8, 4), index=dates, columns=['A', 'B', 'C', 'D'])
In [3]: df
Out[3]:
A B C D
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632
2000-01-02 1.212112 -0.173215 0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804
2000-01-04 0.721555 -0.706771 -1.039575 0.271860
2000-01-05 -0.424972 0.567020 0.276232 -1.087401
2000-01-06 -0.673690 0.113648 -1.478427 0.524988
2000-01-07 0.404705 0.577046 -1.715002 -1.039268
2000-01-08 -0.370647 -1.157892 -1.344312 0.844885
In [4]: panel = pd.Panel({'one' : df, 'two' : df - df.mean()})
In [5]: panel
Out[5]:
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 8 (major_axis) x 4 (minor_axis)
Items axis: one to two
Major_axis axis: 2000-01-01 00:00:00 to 2000-01-08 00:00:00
Minor_axis axis: A to D
Примечание
Ни одна из функций индексирования не является специфичной для временных рядов, если не указано иное.
Таким образом, как указано выше, у нас есть самое базовое индексирование с использованием []:
In [6]: s = df['A']
In [7]: s[dates[5]]
Out[7]: -0.67368970808837059
In [8]: panel['two']
Out[8]:
A B C D
2000-01-01 0.409571 0.113086 -0.610826 -0.936507
2000-01-02 1.152571 0.222735 1.017442 -0.845111
2000-01-03 -0.921390 -1.708620 0.403304 1.270929
2000-01-04 0.662014 -0.310822 -0.141342 0.470985
2000-01-05 -0.484513 0.962970 1.174465 -0.888276
2000-01-06 -0.733231 0.509598 -0.580194 0.724113
2000-01-07 0.345164 0.972995 -0.816769 -0.840143
2000-01-08 -0.430188 -0.761943 -0.446079 1.044010
Вы можете передать список столбцов в [], чтобы выбрать столбцы в этом порядке. Если столбец не содержится в DataFrame, будет вызвано исключение. Несколько столбцов также могут быть установлены таким образом:
In [9]: df
Out[9]:
A B C D
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632
2000-01-02 1.212112 -0.173215 0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804
2000-01-04 0.721555 -0.706771 -1.039575 0.271860
2000-01-05 -0.424972 0.567020 0.276232 -1.087401
2000-01-06 -0.673690 0.113648 -1.478427 0.524988
2000-01-07 0.404705 0.577046 -1.715002 -1.039268
2000-01-08 -0.370647 -1.157892 -1.344312 0.844885
In [10]: df[['B', 'A']] = df[['A', 'B']]
In [11]: df
Out[11]:
A B C D
2000-01-01 -0.282863 0.469112 -1.509059 -1.135632
2000-01-02 -0.173215 1.212112 0.119209 -1.044236
2000-01-03 -2.104569 -0.861849 -0.494929 1.071804
2000-01-04 -0.706771 0.721555 -1.039575 0.271860
2000-01-05 0.567020 -0.424972 0.276232 -1.087401
2000-01-06 0.113648 -0.673690 -1.478427 0.524988
2000-01-07 0.577046 0.404705 -1.715002 -1.039268
2000-01-08 -1.157892 -0.370647 -1.344312 0.844885
Это может быть полезно для применения преобразования (на месте) к подмножеству столбцов.
Предупреждение
pandas выравнивает все ОСИ при установке Series и DataFrame из .loc, .iloc и .ix.
Это не изменит df, потому что выравнивание столбцов происходит перед присвоением значений.
In [12]: df[['A', 'B']]
Out[12]:
A B
2000-01-01 -0.282863 0.469112
2000-01-02 -0.173215 1.212112
2000-01-03 -2.104569 -0.861849
2000-01-04 -0.706771 0.721555
2000-01-05 0.567020 -0.424972
2000-01-06 0.113648 -0.673690
2000-01-07 0.577046 0.404705
2000-01-08 -1.157892 -0.370647
In [13]: df.loc[:,['B', 'A']] = df[['A', 'B']]
In [14]: df[['A', 'B']]
Out[14]:
A B
2000-01-01 -0.282863 0.469112
2000-01-02 -0.173215 1.212112
2000-01-03 -2.104569 -0.861849
2000-01-04 -0.706771 0.721555
2000-01-05 0.567020 -0.424972
2000-01-06 0.113648 -0.673690
2000-01-07 0.577046 0.404705
2000-01-08 -1.157892 -0.370647
Правильный способ — использовать исходные значения
In [15]: df.loc[:,['B', 'A']] = df[['A', 'B']].values
In [16]: df[['A', 'B']]
Out[16]:
A B
2000-01-01 0.469112 -0.282863
2000-01-02 1.212112 -0.173215
2000-01-03 -0.861849 -2.104569
2000-01-04 0.721555 -0.706771
2000-01-05 -0.424972 0.567020
2000-01-06 -0.673690 0.113648
2000-01-07 0.404705 0.577046
2000-01-08 -0.370647 -1.157892
Доступ к атрибутам
Вы можете получить доступ к индексу Series, столбцу DataFrame и элементу Panel напрямую как атрибут:
In [17]: sa = pd.Series([1,2,3],index=list('abc'))
In [18]: dfa = df.copy()
In [19]: sa.b
Out[19]: 2
In [20]: dfa.A
Out[20]:
2000-01-01 0.469112
2000-01-02 1.212112
2000-01-03 -0.861849
2000-01-04 0.721555
2000-01-05 -0.424972
2000-01-06 -0.673690
2000-01-07 0.404705
2000-01-08 -0.370647
Freq: D, Name: A, dtype: float64
In [21]: panel.one
Out[21]:
A B C D
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632
2000-01-02 1.212112 -0.173215 0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804
2000-01-04 0.721555 -0.706771 -1.039575 0.271860
2000-01-05 -0.424972 0.567020 0.276232 -1.087401
2000-01-06 -0.673690 0.113648 -1.478427 0.524988
2000-01-07 0.404705 0.577046 -1.715002 -1.039268
2000-01-08 -0.370647 -1.157892 -1.344312 0.844885
Вы можете использовать доступ к атрибуту для изменения существующего элемента Series или столбца DataFrame, но будьте осторожны: если вы попытаетесь использовать доступ к атрибуту для создания нового столбца, это произойдет без сообщений об ошибках, создавая новый атрибут вместо нового столбца.
In [22]: sa.a = 5
In [23]: sa
Out[23]:
a 5
b 2
c 3
dtype: int64
In [24]: dfa.A = list(range(len(dfa.index))) # ok if A already exists
In [25]: dfa
Out[25]:
A B C D
2000-01-01 0 -0.282863 -1.509059 -1.135632
2000-01-02 1 -0.173215 0.119209 -1.044236
2000-01-03 2 -2.104569 -0.494929 1.071804
2000-01-04 3 -0.706771 -1.039575 0.271860
2000-01-05 4 0.567020 0.276232 -1.087401
2000-01-06 5 0.113648 -1.478427 0.524988
2000-01-07 6 0.577046 -1.715002 -1.039268
2000-01-08 7 -1.157892 -1.344312 0.844885
In [26]: dfa['A'] = list(range(len(dfa.index))) # use this form to create a new column
In [27]: dfa
Out[27]:
A B C D
2000-01-01 0 -0.282863 -1.509059 -1.135632
2000-01-02 1 -0.173215 0.119209 -1.044236
2000-01-03 2 -2.104569 -0.494929 1.071804
2000-01-04 3 -0.706771 -1.039575 0.271860
2000-01-05 4 0.567020 0.276232 -1.087401
2000-01-06 5 0.113648 -1.478427 0.524988
2000-01-07 6 0.577046 -1.715002 -1.039268
2000-01-08 7 -1.157892 -1.344312 0.844885
END_OF_DOCUMENT_MARKER Предупреждение
- Вы можете использовать этот доступ только в том случае, если элемент индекса является допустимым идентификатором Python, например,
s.1не разрешен. См. здесь для объяснения допустимых идентификаторов. - Атрибут не будет доступен, если он конфликтует с существующим именем метода, например,
s.minне разрешен. - Аналогично, атрибут не будет доступен, если он конфликтует с любым из следующих элементов списка:
index,major_axis,minor_axis,items,labels. - В любом из этих случаев стандартная индексация по-прежнему будет работать, например,
s['1'],s['min']иs['index']будут обращаться к соответствующему элементу или столбцу. Series/Panelдоступны начиная с версии 0.13.0.
Если вы используете среду IPython, вы также можете использовать автодополнение, чтобы увидеть доступные атрибуты.
Вы также можете назначить dict строке DataFrame:
In [28]: x = pd.DataFrame({'x': [1, 2, 3], 'y': [3, 4, 5]})
In [29]: x.iloc[1] = dict(x=9, y=99)
In [30]: x
Out[30]:
x y
0 1 3
1 9 99
2 3 5
Диапазоны срезов
Наиболее надежный и согласованный способ среза по произвольным осям описан в разделе Выбор по позиции, который подробно описывает метод .iloc. На данный момент мы объясняем семантику среза с помощью оператора [].
В Series синтаксис работает точно так же, как и с ndarray, возвращая срез значений и соответствующих меток:
In [31]: s[:5] Out[31]: 2000-01-01 0.469112 2000-01-02 1.212112 2000-01-03 -0.861849 2000-01-04 0.721555 2000-01-05 -0.424972 Freq: D, Name: A, dtype: float64 In [32]: s[::2] Out[32]: 2000-01-01 0.469112 2000-01-03 -0.861849 2000-01-05 -0.424972 2000-01-07 0.404705 Freq: 2D, Name: A, dtype: float64 In [33]: s[::-1] Out[33]: 2000-01-08 -0.370647 2000-01-07 0.404705 2000-01-06 -0.673690 2000-01-05 -0.424972 2000-01-04 0.721555 2000-01-03 -0.861849 2000-01-02 1.212112 2000-01-01 0.469112 Freq: -1D, Name: A, dtype: float64
Обратите внимание, что установка также работает:
In [34]: s2 = s.copy() In [35]: s2[:5] = 0 In [36]: s2 Out[36]: 2000-01-01 0.000000 2000-01-02 0.000000 2000-01-03 0.000000 2000-01-04 0.000000 2000-01-05 0.000000 2000-01-06 -0.673690 2000-01-07 0.404705 2000-01-08 -0.370647 Freq: D, Name: A, dtype: float64
В DataFrame срез внутри [] вырезает строки. Это в значительной степени обеспечивается удобством, так как это такая распространенная операция.
In [37]: df[:3]
Out[37]:
A B C D
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632
2000-01-02 1.212112 -0.173215 0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804
In [38]: df[::-1]
Out[38]:
A B C D
2000-01-08 -0.370647 -1.157892 -1.344312 0.844885
2000-01-07 0.404705 0.577046 -1.715002 -1.039268
2000-01-06 -0.673690 0.113648 -1.478427 0.524988
2000-01-05 -0.424972 0.567020 0.276232 -1.087401
2000-01-04 0.721555 -0.706771 -1.039575 0.271860
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804
2000-01-02 1.212112 -0.173215 0.119209 -1.044236
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632
Выбор по метке
Предупреждение
Возвращаемая при операции установки копия или ссылка могут зависеть от контекста. Это иногда называется chained assignment и следует избегать. См. Возвращение представления или копии
Предупреждение
.loc строго относится к слайсерам, которые не совместимы (или не могут быть преобразованы) с типом индекса. Например, использование целых чисел в DatetimeIndex. Это приведет к исключению TypeError.
In [39]: dfl = pd.DataFrame(np.random.randn(5,4), columns=list('ABCD'), index=pd.date_range('20130101',periods=5))
In [40]: dfl
Out[40]:
A B C D
2013-01-01 1.075770 -0.109050 1.643563 -1.469388
2013-01-02 0.357021 -0.674600 -1.776904 -0.968914
2013-01-03 -1.294524 0.413738 0.276662 -0.472035
2013-01-04 -0.013960 -0.362543 -0.006154 -0.923061
2013-01-05 0.895717 0.805244 -1.206412 2.565646
In [4]: dfl.loc[2:3] TypeError: cannot do slice indexing on <class 'pandas.tseries.index.DatetimeIndex'> with these indexers [2] of <type 'int'>
Строковые значения в срезах могут быть преобразуемы в тип индекса и привести к естественному срезу.
In [41]: dfl.loc['20130102':'20130104']
Out[41]:
A B C D
2013-01-02 0.357021 -0.674600 -1.776904 -0.968914
2013-01-03 -1.294524 0.413738 0.276662 -0.472035
2013-01-04 -0.013960 -0.362543 -0.006154 -0.923061
pandas предоставляет набор методов для чисто метки индексации. Это протокол строгого включения. Хотя бы 1 из меток, которые вы запрашиваете, должна быть в индексе, в противном случае будет вызвано исключение KeyError! При срезе начальная граница включена, и конечная граница включена. Целые числа являются допустимыми метками, но они относятся к метке, а не к позиции.
Атрибут .loc является основным методом доступа. Допустимыми входными данными являются:
- Одна метка, например,
5или'a'(обратите внимание, что5интерпретируется как метка индекса. Это использование не является позицией целого числа по индексу) - Список или массив меток
['a', 'b', 'c'] - Объект среза с метками
'a':'f'(обратите внимание, что в отличие от обычных срезов Python, обе начальная и конечная границы включены!) - Массив булевых значений
callable, см. Выбор по вызываемому объекту
In [42]: s1 = pd.Series(np.random.randn(6),index=list('abcdef'))
In [43]: s1
Out[43]:
a 1.431256
b 1.340309
c -1.170299
d -0.226169
e 0.410835
f 0.813850
dtype: float64
In [44]: s1.loc['c':]
Out[44]:
c -1.170299
d -0.226169
e 0.410835
f 0.813850
dtype: float64
In [45]: s1.loc['b']
Out[45]: 1.3403088497993827
Обратите внимание, что установка также работает:
In [46]: s1.loc['c':] = 0 In [47]: s1 Out[47]: a 1.431256 b 1.340309 c 0.000000 d 0.000000 e 0.000000 f 0.000000 dtype: float64
С DataFrame
In [48]: df1 = pd.DataFrame(np.random.randn(6,4),
....: index=list('abcdef'),
....: columns=list('ABCD'))
....:
In [49]: df1
Out[49]:
A B C D
a 0.132003 -0.827317 -0.076467 -1.187678
b 1.130127 -1.436737 -1.413681 1.607920
c 1.024180 0.569605 0.875906 -2.211372
d 0.974466 -2.006747 -0.410001 -0.078638
e 0.545952 -1.219217 -1.226825 0.769804
f -1.281247 -0.727707 -0.121306 -0.097883
In [50]: df1.loc[['a', 'b', 'd'], :]
Out[50]:
A B C D
a 0.132003 -0.827317 -0.076467 -1.187678
b 1.130127 -1.436737 -1.413681 1.607920
d 0.974466 -2.006747 -0.410001 -0.078638
Доступ через срезы меток
In [51]: df1.loc['d':, 'A':'C']
Out[51]:
A B C
d 0.974466 -2.006747 -0.410001
e 0.545952 -1.219217 -1.226825
f -1.281247 -0.727707 -0.121306
Для получения поперечного сечения с помощью метки (эквивалентно df.xs('a'))
In [52]: df1.loc['a'] Out[52]: A 0.132003 B -0.827317 C -0.076467 D -1.187678 Name: a, dtype: float64
Для получения значений с помощью массива булевых значений
In [53]: df1.loc['a'] > 0
Out[53]:
A True
B False
C False
D False
Name: a, dtype: bool
In [54]: df1.loc[:, df1.loc['a'] > 0]
Out[54]:
A
a 0.132003
b 1.130127
c 1.024180
d 0.974466
e 0.545952
f -1.281247
Для получения значения явно (эквивалентно устаревшему df.get_value('a','A'))
# this is also equivalent to ``df1.at['a','A']`` In [55]: df1.loc['a', 'A'] Out[55]: 0.13200317033032932
Выбор по позиции
Предупреждение
Возвращаемая при операции установки копия или ссылка могут зависеть от контекста. Это иногда называется chained assignment и следует избегать. См. Возвращение представления или копии
pandas предоставляет набор методов для получения чисто целочисленной индексации. Семантика тесно следует синтаксису срезов Python и numpy. Это целочисленная индексация. При срезе начальная граница включена, а верхняя граница исключена. Попытка использовать не целое число, даже допустимую метку, вызовет исключение IndexError.
Атрибут .iloc является основным методом доступа. Допустимыми входными данными являются:
- Целое число, например,
5 - Список или массив целых чисел
[4, 3, 0] - Объект среза с целыми числами
1:7 - Массив булевых значений
callable, см. Выбор по вызываемому объекту
In [56]: s1 = pd.Series(np.random.randn(5), index=list(range(0,10,2))) In [57]: s1 Out[57]: 0 0.695775 2 0.341734 4 0.959726 6 -1.110336 8 -0.619976 dtype: float64 In [58]: s1.iloc[:3] Out[58]: 0 0.695775 2 0.341734 4 0.959726 dtype: float64 In [59]: s1.iloc[3] Out[59]: -1.1103361028911669
Обратите внимание, что установка также работает:
In [60]: s1.iloc[:3] = 0 In [61]: s1 Out[61]: 0 0.000000 2 0.000000 4 0.000000 6 -1.110336 8 -0.619976 dtype: float64
С DataFrame
In [62]: df1 = pd.DataFrame(np.random.randn(6,4),
....: index=list(range(0,12,2)),
....: columns=list(range(0,8,2)))
....:
In [63]: df1
Out[63]:
0 2 4 6
0 0.149748 -0.732339 0.687738 0.176444
2 0.403310 -0.154951 0.301624 -2.179861
4 -1.369849 -0.954208 1.462696 -1.743161
6 -0.826591 -0.345352 1.314232 0.690579
8 0.995761 2.396780 0.014871 3.357427
10 -0.317441 -1.236269 0.896171 -0.487602
Выбор с помощью целочисленного среза
In [64]: df1.iloc[:3]
Out[64]:
0 2 4 6
0 0.149748 -0.732339 0.687738 0.176444
2 0.403310 -0.154951 0.301624 -2.179861
4 -1.369849 -0.954208 1.462696 -1.743161
In [65]: df1.iloc[1:5, 2:4]
Out[65]:
4 6
2 0.301624 -2.179861
4 1.462696 -1.743161
6 1.314232 0.690579
8 0.014871 3.357427
Выбор с помощью целочисленного списка
In [66]: df1.iloc[[1, 3, 5], [1, 3]]
Out[66]:
2 6
2 -0.154951 -2.179861
6 -0.345352 0.690579
10 -1.236269 -0.487602
In [67]: df1.iloc[1:3, :]
Out[67]:
0 2 4 6
2 0.403310 -0.154951 0.301624 -2.179861
4 -1.369849 -0.954208 1.462696 -1.743161
In [68]: df1.iloc[:, 1:3]
Out[68]:
2 4
0 -0.732339 0.687738
2 -0.154951 0.301624
4 -0.954208 1.462696
6 -0.345352 1.314232
8 2.396780 0.014871
10 -1.236269 0.896171
# this is also equivalent to ``df1.iat[1,1]`` In [69]: df1.iloc[1, 1] Out[69]: -0.15495077442490321
Для получения поперечного сечения с помощью целочисленной позиции (эквивалентно df.xs(1))
In [70]: df1.iloc[1] Out[70]: 0 0.403310 2 -0.154951 4 0.301624 6 -2.179861 Name: 2, dtype: float64
Индексы срезов вне диапазона обрабатываются так же, как и в Python/Numpy.
# these are allowed in python/numpy.
# Only works in Pandas starting from v0.14.0.
In [71]: x = list('abcdef')
In [72]: x
Out[72]: ['a', 'b', 'c', 'd', 'e', 'f']
In [73]: x[4:10]
Out[73]: ['e', 'f']
In [74]: x[8:10]
Out[74]: []
In [75]: s = pd.Series(x)
In [76]: s
Out[76]:
0 a
1 b
2 c
3 d
4 e
5 f
dtype: object
In [77]: s.iloc[4:10]
Out[77]:
4 e
5 f
dtype: object
In [78]: s.iloc[8:10]
Out[78]: Series([], dtype: object)
Примечание
До версии v0.14.0, iloc не принимал индексаторы вне границ для срезов, например, значение, превышающее длину индексируемого объекта.
Обратите внимание, что это может привести к пустой оси (например, возвращается пустой DataFrame).
In [79]: dfl = pd.DataFrame(np.random.randn(5,2), columns=list('AB'))
In [80]: dfl
Out[80]:
A B
0 -0.082240 -2.182937
1 0.380396 0.084844
2 0.432390 1.519970
3 -0.493662 0.600178
4 0.274230 0.132885
In [81]: dfl.iloc[:, 2:3]
Out[81]:
Empty DataFrame
Columns: []
Index: [0, 1, 2, 3, 4]
In [82]: dfl.iloc[:, 1:3]
Out[82]:
B
0 -2.182937
1 0.084844
2 1.519970
3 0.600178
4 0.132885
In [83]: dfl.iloc[4:6]
Out[83]:
A B
4 0.27423 0.132885
Один индексатор, находящийся вне границ, вызовет исключение IndexError. Список индексаторов, где любой элемент находится вне границ, вызовет исключение IndexError.
dfl.iloc[[4, 5, 6]] IndexError: positional indexers are out-of-bounds dfl.iloc[:, 4] IndexError: single positional indexer is out-of-bounds
Выбор по вызываемому объекту
Новое в версии 0.18.1.
.loc, .iloc, .ix и также [] индексация могут принимать callable в качестве индексатора. callable должен быть функцией с одним аргументом (вызываемый Series, DataFrame или Panel), которая возвращает допустимый результат для индексации.
In [84]: df1 = pd.DataFrame(np.random.randn(6, 4),
....: index=list('abcdef'),
....: columns=list('ABCD'))
....:
In [85]: df1
Out[85]:
A B C D
a -0.023688 2.410179 1.450520 0.206053
b -0.251905 -2.213588 1.063327 1.266143
c 0.299368 -0.863838 0.408204 -1.048089
d -0.025747 -0.988387 0.094055 1.262731
e 1.289997 0.082423 -0.055758 0.536580
f -0.489682 0.369374 -0.034571 -2.484478
In [86]: df1.loc[lambda df: df.A > 0, :]
Out[86]:
A B C D
c 0.299368 -0.863838 0.408204 -1.048089
e 1.289997 0.082423 -0.055758 0.536580
In [87]: df1.loc[:, lambda df: ['A', 'B']]
Out[87]:
A B
a -0.023688 2.410179
b -0.251905 -2.213588
c 0.299368 -0.863838
d -0.025747 -0.988387
e 1.289997 0.082423
f -0.489682 0.369374
In [88]: df1.iloc[:, lambda df: [0, 1]]
Out[88]:
A B
a -0.023688 2.410179
b -0.251905 -2.213588
c 0.299368 -0.863838
d -0.025747 -0.988387
e 1.289997 0.082423
f -0.489682 0.369374
In [89]: df1[lambda df: df.columns[0]]
Out[89]:
a -0.023688
b -0.251905
c 0.299368
d -0.025747
e 1.289997
f -0.489682
Name: A, dtype: float64
Вы можете использовать индексацию вызываемого объекта в Series.
In [90]: df1.A.loc[lambda s: s > 0] Out[90]: c 0.299368 e 1.289997 Name: A, dtype: float64
Используя эти методы/индексаторы, вы можете цепочкой операции выбора данных без использования временных переменных.
In [91]: bb = pd.read_csv('data/baseball.csv', index_col='id')
In [92]: (bb.groupby(['year', 'team']).sum()
....: .loc[lambda df: df.r > 100])
....:
Out[92]:
stint g ab r h X2b X3b hr rbi sb cs bb \
year team
2007 CIN 6 379 745 101 203 35 2 36 125.0 10.0 1.0 105
DET 5 301 1062 162 283 54 4 37 144.0 24.0 7.0 97
HOU 4 311 926 109 218 47 6 14 77.0 10.0 4.0 60
LAN 11 413 1021 153 293 61 3 36 154.0 7.0 5.0 114
NYN 13 622 1854 240 509 101 3 61 243.0 22.0 4.0 174
SFN 5 482 1305 198 337 67 6 40 171.0 26.0 7.0 235
TEX 2 198 729 115 200 40 4 28 115.0 21.0 4.0 73
TOR 4 459 1408 187 378 96 2 58 223.0 4.0 2.0 190
so ibb hbp sh sf gidp
year team
2007 CIN 127.0 14.0 1.0 1.0 15.0 18.0
DET 176.0 3.0 10.0 4.0 8.0 28.0
HOU 212.0 3.0 9.0 16.0 6.0 17.0
LAN 141.0 8.0 9.0 3.0 8.0 29.0
NYN 310.0 24.0 23.0 18.0 15.0 48.0
SFN 188.0 51.0 8.0 16.0 6.0 41.0
TEX 140.0 4.0 5.0 2.0 8.0 16.0
TOR 265.0 16.0 12.0 4.0 16.0 38.0
Выбор случайных выборок
Случайный выбор строк или столбцов из Series, DataFrame или Panel с помощью метода sample(). Метод по умолчанию выбирает строки и принимает определенное количество строк/столбцов для возврата или долю строк.
In [93]: s = pd.Series([0,1,2,3,4,5]) # When no arguments are passed, returns 1 row. In [94]: s.sample() Out[94]: 4 4 dtype: int64 # One may specify either a number of rows: In [95]: s.sample(n=3) Out[95]: 0 0 4 4 1 1 dtype: int64 # Or a fraction of the rows: In [96]: s.sample(frac=0.5) Out[96]: 5 5 3 3 1 1 dtype: int64
По умолчанию sample вернет каждую строку не более одного раза, но также можно выбрать с заменой с помощью опции replace:
In [97]: s = pd.Series([0,1,2,3,4,5]) # Without replacement (default): In [98]: s.sample(n=6, replace=False) Out[98]: 0 0 1 1 5 5 3 3 2 2 4 4 dtype: int64 # With replacement: In [99]: s.sample(n=6, replace=True) Out[99]: 0 0 4 4 3 3 2 2 4 4 4 4 dtype: int64
По умолчанию каждая строка имеет равную вероятность выбора, но если вы хотите, чтобы строки имели разные вероятности, вы можете передать функцию sample, взвешивающую выборку, как weights. Эти веса могут быть списком, массивом numpy или Series, но они должны иметь одинаковую длину с объектом, который вы выбираете. Пропущенные значения будут обрабатываться как вес 0, а значения inf не допускаются. Если веса не суммируются до 1, они будут перенормированы, разделив все веса на сумму весов. Например:
In [100]: s = pd.Series([0,1,2,3,4,5]) In [101]: example_weights = [0, 0, 0.2, 0.2, 0.2, 0.4] In [102]: s.sample(n=3, weights=example_weights) Out[102]: 5 5 4 4 3 3 dtype: int64 # Weights will be re-normalized automatically In [103]: example_weights2 = [0.5, 0, 0, 0, 0, 0] In [104]: s.sample(n=1, weights=example_weights2) Out[104]: 0 0 dtype: int64
При применении к DataFrame вы можете использовать столбец DataFrame в качестве весов выборки (при условии, что вы выбираете строки, а не столбцы), просто передав имя столбца как строку.
In [105]: df2 = pd.DataFrame({'col1':[9,8,7,6], 'weight_column':[0.5, 0.4, 0.1, 0]})
In [106]: df2.sample(n = 3, weights = 'weight_column')
Out[106]:
col1 weight_column
1 8 0.4
0 9 0.5
2 7 0.1
sample также позволяет пользователям выбирать столбцы вместо строк с помощью аргумента axis.
In [107]: df3 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})
In [108]: df3.sample(n=1, axis=1)
Out[108]:
col1
0 1
1 2
2 3
Наконец, можно также установить семя генератора случайных чисел sample с помощью аргумента random_state, который будет принимать либо целое число (как семя), либо объект numpy RandomState.
In [109]: df4 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})
# With a given seed, the sample will always draw the same rows.
In [110]: df4.sample(n=2, random_state=2)
Out[110]:
col1 col2
2 3 4
1 2 3
In [111]: df4.sample(n=2, random_state=2)
Out[111]:
col1 col2
2 3 4
1 2 3
Установка с расширением
Новое в версии 0.13.
Операции .loc/.ix/[] могут выполнять расширение при установке несуществующего ключа для данной оси.
В случае Series это фактически операция добавления
In [112]: se = pd.Series([1,2,3]) In [113]: se Out[113]: 0 1 1 2 2 3 dtype: int64 In [114]: se[5] = 5. In [115]: se Out[115]: 0 1.0 1 2.0 2 3.0 5 5.0 dtype: float64
DataFrame можно расширить по любой оси с помощью .loc
In [116]: dfi = pd.DataFrame(np.arange(6).reshape(3,2), .....: columns=['A','B']) .....: In [117]: dfi Out[117]: A B 0 0 1 1 2 3 2 4 5 In [118]: dfi.loc[:,'C'] = dfi.loc[:,'A'] In [119]: dfi Out[119]: A B C 0 0 1 0 1 2 3 2 2 4 5 4
Это похоже на операцию append на DataFrame.
In [120]: dfi.loc[3] = 5 In [121]: dfi Out[121]: A B C 0 0 1 0 1 2 3 2 2 4 5 4 3 5 5 5
Быстрое получение и установка скалярных значений
Поскольку индексация с [] должна обрабатывать множество случаев (доступ к одному значению, срез, индексация булевых значений и т. д.), она имеет некоторую издержки для выяснения того, что вы запрашиваете. Если вам нужно получить только скалярное значение, самый быстрый способ - использовать методы at и iat, которые реализованы во всех структурах данных.
Аналогично loc, at обеспечивает метки скалярный поиск, в то время как iat обеспечивает целочисленный поиск аналогично iloc
In [122]: s.iat[5] Out[122]: 5 In [123]: df.at[dates[5], 'A'] Out[123]: -0.67368970808837059 In [124]: df.iat[3, 0] Out[124]: 0.72155516224436689
Вы также можете установить с использованием тех же индексаторов.
In [125]: df.at[dates[5], 'E'] = 7 In [126]: df.iat[3, 0] = 7
at может расширить объект на месте, как указано выше, если индексатор отсутствует.
In [127]: df.at[dates[-1]+1, 0] = 7
In [128]: df
Out[128]:
A B C D E 0
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632 NaN NaN
2000-01-02 1.212112 -0.173215 0.119209 -1.044236 NaN NaN
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804 NaN NaN
2000-01-04 7.000000 -0.706771 -1.039575 0.271860 NaN NaN
2000-01-05 -0.424972 0.567020 0.276232 -1.087401 NaN NaN
2000-01-06 -0.673690 0.113648 -1.478427 0.524988 7.0 NaN
2000-01-07 0.404705 0.577046 -1.715002 -1.039268 NaN NaN
2000-01-08 -0.370647 -1.157892 -1.344312 0.844885 NaN NaN
2000-01-09 NaN NaN NaN NaN NaN 7.0
Булевы индексы
Ещё одной распространённой операцией является использование булевых векторов для фильтрации данных. Операторы: | для or, & для and и ~ для not. Их необходимо группировать с помощью скобок.
Использование булевого вектора для индексации Series работает точно так же, как в numpy ndarray:
In [129]: s = pd.Series(range(-3, 4)) In [130]: s Out[130]: 0 -3 1 -2 2 -1 3 0 4 1 5 2 6 3 dtype: int64 In [131]: s[s > 0] Out[131]: 4 1 5 2 6 3 dtype: int64 In [132]: s[(s < -1) | (s > 0.5)] Out[132]: 0 -3 1 -2 4 1 5 2 6 3 dtype: int64 In [133]: s[~(s < 0)] Out[133]: 3 0 4 1 5 2 6 3 dtype: int64
Вы можете выбрать строки из DataFrame, используя булевый вектор такой же длины, как индекс DataFrame (например, что-то полученное из одного из столбцов DataFrame):
In [134]: df[df['A'] > 0]
Out[134]:
A B C D E 0
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632 NaN NaN
2000-01-02 1.212112 -0.173215 0.119209 -1.044236 NaN NaN
2000-01-04 7.000000 -0.706771 -1.039575 0.271860 NaN NaN
2000-01-07 0.404705 0.577046 -1.715002 -1.039268 NaN NaN
Для создания более сложных критериев также можно использовать списковые включения и метод map Series:
In [135]: df2 = pd.DataFrame({'a' : ['one', 'one', 'two', 'three', 'two', 'one', 'six'],
.....: 'b' : ['x', 'y', 'y', 'x', 'y', 'x', 'x'],
.....: 'c' : np.random.randn(7)})
.....:
# only want 'two' or 'three'
In [136]: criterion = df2['a'].map(lambda x: x.startswith('t'))
In [137]: df2[criterion]
Out[137]:
a b c
2 two y 0.041290
3 three x 0.361719
4 two y -0.238075
# equivalent but slower
In [138]: df2[[x.startswith('t') for x in df2['a']]]
Out[138]:
a b c
2 two y 0.041290
3 three x 0.361719
4 two y -0.238075
# Multiple criteria
In [139]: df2[criterion & (df2['b'] == 'x')]
Out[139]:
a b c
3 three x 0.361719
Обратите внимание, что с помощью методов выбора Выбор по метке, Выбор по позиции и Расширенная индексация вы можете выбирать по нескольким осям, используя булевые векторы в сочетании с другими выражениями индексации.
In [140]: df2.loc[criterion & (df2['b'] == 'x'),'b':'c'] Out[140]: b c 3 x 0.361719
Индексация с помощью isin
Рассмотрим метод isin Series, который возвращает булевый вектор, равный true, где элементы Series существуют в переданном списке. Это позволяет вам выбирать строки, где один или несколько столбцов имеют значения, которые вы хотите:
In [141]: s = pd.Series(np.arange(5), index=np.arange(5)[::-1], dtype='int64') In [142]: s Out[142]: 4 0 3 1 2 2 1 3 0 4 dtype: int64 In [143]: s.isin([2, 4, 6]) Out[143]: 4 False 3 False 2 True 1 False 0 True dtype: bool In [144]: s[s.isin([2, 4, 6])] Out[144]: 2 2 0 4 dtype: int64
Тот же метод доступен для объектов Index и полезен в тех случаях, когда вы не знаете, какие из искомых меток на самом деле присутствуют:
In [145]: s[s.index.isin([2, 4, 6])] Out[145]: 4 0 2 2 dtype: int64 # compare it to the following In [146]: s[[2, 4, 6]] Out[146]: 2 2.0 4 0.0 6 NaN dtype: float64
Кроме того, MultiIndex позволяет выбрать отдельный уровень для использования в проверке принадлежности:
In [147]: s_mi = pd.Series(np.arange(6), .....: index=pd.MultiIndex.from_product([[0, 1], ['a', 'b', 'c']])) .....: In [148]: s_mi Out[148]: 0 a 0 b 1 c 2 1 a 3 b 4 c 5 dtype: int64 In [149]: s_mi.iloc[s_mi.index.isin([(1, 'a'), (2, 'b'), (0, 'c')])] Out[149]: 0 c 2 1 a 3 dtype: int64 In [150]: s_mi.iloc[s_mi.index.isin(['a', 'c', 'e'], level=1)] Out[150]: 0 a 0 c 2 1 a 3 c 5 dtype: int64
DataFrame также имеет метод isin. При вызове isin передайте набор значений в виде массива или словаря. Если values — массив, isin возвращает DataFrame булевых значений, имеющий такую же форму, что и исходный DataFrame, с True там, где элемент есть в последовательности значений.
In [151]: df = pd.DataFrame({'vals': [1, 2, 3, 4], 'ids': ['a', 'b', 'f', 'n'],
.....: 'ids2': ['a', 'n', 'c', 'n']})
.....:
In [152]: values = ['a', 'b', 1, 3]
In [153]: df.isin(values)
Out[153]:
ids ids2 vals
0 True True True
1 True False False
2 False False True
3 False False False
Часто вы хотите сопоставить определённые значения с определёнными столбцами. Просто сделайте values словарем, где ключ — столбец, а значение — список элементов, которые вы хотите проверить.
In [154]: values = {'ids': ['a', 'b'], 'vals': [1, 3]}
In [155]: df.isin(values)
Out[155]:
ids ids2 vals
0 True False True
1 True False False
2 False False True
3 False False False
Объедините метод isin DataFrame с методами any() и all(), чтобы быстро выбрать подмножества данных, удовлетворяющих заданным критериям. Чтобы выбрать строку, где каждый столбец удовлетворяет своему критерию:
In [156]: values = {'ids': ['a', 'b'], 'ids2': ['a', 'c'], 'vals': [1, 3]}
In [157]: row_mask = df.isin(values).all(1)
In [158]: df[row_mask]
Out[158]:
ids ids2 vals
0 a a 1
Метод where() и маскирование
Выбор значений из Series с помощью булевого вектора обычно возвращает подмножество данных. Чтобы гарантировать, что результат выбора имеет ту же форму, что и исходные данные, вы можете использовать метод where в Series и DataFrame.
Чтобы вернуть только выбранные строки
In [159]: s[s > 0] Out[159]: 3 1 2 2 1 3 0 4 dtype: int64
Чтобы вернуть Series той же формы, что и исходная
In [160]: s.where(s > 0) Out[160]: 4 NaN 3 1.0 2 2.0 1 3.0 0 4.0 dtype: float64
Выбор значений из DataFrame с булевым критерием теперь также сохраняет форму входных данных. where используется в качестве реализации. Эквивалентно df.where(df < 0)
In [161]: df[df < 0]
Out[161]:
A B C D
2000-01-01 -2.104139 -1.309525 NaN NaN
2000-01-02 -0.352480 NaN -1.192319 NaN
2000-01-03 -0.864883 NaN -0.227870 NaN
2000-01-04 NaN -1.222082 NaN -1.233203
2000-01-05 NaN -0.605656 -1.169184 NaN
2000-01-06 NaN -0.948458 NaN -0.684718
2000-01-07 -2.670153 -0.114722 NaN -0.048048
2000-01-08 NaN NaN -0.048788 -0.808838
Кроме того, where принимает необязательный аргумент other для замены значений, где условие ложно, в возвращённой копии.
In [162]: df.where(df < 0, -df)
Out[162]:
A B C D
2000-01-01 -2.104139 -1.309525 -0.485855 -0.245166
2000-01-02 -0.352480 -0.390389 -1.192319 -1.655824
2000-01-03 -0.864883 -0.299674 -0.227870 -0.281059
2000-01-04 -0.846958 -1.222082 -0.600705 -1.233203
2000-01-05 -0.669692 -0.605656 -1.169184 -0.342416
2000-01-06 -0.868584 -0.948458 -2.297780 -0.684718
2000-01-07 -2.670153 -0.114722 -0.168904 -0.048048
2000-01-08 -0.801196 -1.392071 -0.048788 -0.808838
Вы можете установить значения на основе некоторых булевых критериев. Это можно сделать интуитивно следующим образом:
In [163]: s2 = s.copy()
In [164]: s2[s2 < 0] = 0
In [165]: s2
Out[165]:
4 0
3 1
2 2
1 3
0 4
dtype: int64
In [166]: df2 = df.copy()
In [167]: df2[df2 < 0] = 0
In [168]: df2
Out[168]:
A B C D
2000-01-01 0.000000 0.000000 0.485855 0.245166
2000-01-02 0.000000 0.390389 0.000000 1.655824
2000-01-03 0.000000 0.299674 0.000000 0.281059
2000-01-04 0.846958 0.000000 0.600705 0.000000
2000-01-05 0.669692 0.000000 0.000000 0.342416
2000-01-06 0.868584 0.000000 2.297780 0.000000
2000-01-07 0.000000 0.000000 0.168904 0.000000
2000-01-08 0.801196 1.392071 0.000000 0.000000
По умолчанию where возвращает изменённую копию данных. Существует необязательный параметр inplace, чтобы исходные данные могли быть изменены без создания копии:
In [169]: df_orig = df.copy()
In [170]: df_orig.where(df > 0, -df, inplace=True);
In [171]: df_orig
Out[171]:
A B C D
2000-01-01 2.104139 1.309525 0.485855 0.245166
2000-01-02 0.352480 0.390389 1.192319 1.655824
2000-01-03 0.864883 0.299674 0.227870 0.281059
2000-01-04 0.846958 1.222082 0.600705 1.233203
2000-01-05 0.669692 0.605656 1.169184 0.342416
2000-01-06 0.868584 0.948458 2.297780 0.684718
2000-01-07 2.670153 0.114722 0.168904 0.048048
2000-01-08 0.801196 1.392071 0.048788 0.808838
Примечание
Подпись для DataFrame.where() отличается от numpy.where(). Примерно df1.where(m, df2) эквивалентно np.where(m, df1, df2).
In [172]: df.where(df < 0, -df) == np.where(df < 0, df, -df)
Out[172]:
A B C D
2000-01-01 True True True True
2000-01-02 True True True True
2000-01-03 True True True True
2000-01-04 True True True True
2000-01-05 True True True True
2000-01-06 True True True True
2000-01-07 True True True True
2000-01-08 True True True True
выравнивание
Кроме того, where выравнивает входное булевое условие (ndarray или DataFrame), чтобы была возможна частичная выборка с установлением. Это аналогично частичной установке через .ix (но для содержимого, а не для меток осей)
In [173]: df2 = df.copy()
In [174]: df2[ df2[1:4] > 0 ] = 3
In [175]: df2
Out[175]:
A B C D
2000-01-01 -2.104139 -1.309525 0.485855 0.245166
2000-01-02 -0.352480 3.000000 -1.192319 3.000000
2000-01-03 -0.864883 3.000000 -0.227870 3.000000
2000-01-04 3.000000 -1.222082 3.000000 -1.233203
2000-01-05 0.669692 -0.605656 -1.169184 0.342416
2000-01-06 0.868584 -0.948458 2.297780 -0.684718
2000-01-07 -2.670153 -0.114722 0.168904 -0.048048
2000-01-08 0.801196 1.392071 -0.048788 -0.808838
Введено в версии 0.13.
Where также может принимать параметры axis и level для выравнивания ввода при выполнении where.
In [176]: df2 = df.copy()
In [177]: df2.where(df2>0,df2['A'],axis='index')
Out[177]:
A B C D
2000-01-01 -2.104139 -2.104139 0.485855 0.245166
2000-01-02 -0.352480 0.390389 -0.352480 1.655824
2000-01-03 -0.864883 0.299674 -0.864883 0.281059
2000-01-04 0.846958 0.846958 0.600705 0.846958
2000-01-05 0.669692 0.669692 0.669692 0.342416
2000-01-06 0.868584 0.868584 2.297780 0.868584
2000-01-07 -2.670153 -2.670153 0.168904 -2.670153
2000-01-08 0.801196 1.392071 0.801196 0.801196
Это эквивалентно (но быстрее), чем следующее.
In [178]: df2 = df.copy()
In [179]: df.apply(lambda x, y: x.where(x>0,y), y=df['A'])
Out[179]:
A B C D
2000-01-01 -2.104139 -2.104139 0.485855 0.245166
2000-01-02 -0.352480 0.390389 -0.352480 1.655824
2000-01-03 -0.864883 0.299674 -0.864883 0.281059
2000-01-04 0.846958 0.846958 0.600705 0.846958
2000-01-05 0.669692 0.669692 0.669692 0.342416
2000-01-06 0.868584 0.868584 2.297780 0.868584
2000-01-07 -2.670153 -2.670153 0.168904 -2.670153
2000-01-08 0.801196 1.392071 0.801196 0.801196
Введено в версии 0.18.1.
Where может принимать вызываемую функцию в качестве условия и аргументы other. Функция должна принимать один аргумент (вызываемую Series или DataFrame) и возвращать допустимый результат в качестве условия и аргумента other.
In [180]: df3 = pd.DataFrame({'A': [1, 2, 3],
.....: 'B': [4, 5, 6],
.....: 'C': [7, 8, 9]})
.....:
In [181]: df3.where(lambda x: x > 4, lambda x: x + 10)
Out[181]:
A B C
0 11 14 7
1 12 5 8
2 13 6 9
маска
mask — обратная булева операция для where.
In [182]: s.mask(s >= 0)
Out[182]:
4 NaN
3 NaN
2 NaN
1 NaN
0 NaN
dtype: float64
In [183]: df.mask(df >= 0)
Out[183]:
A B C D
2000-01-01 -2.104139 -1.309525 NaN NaN
2000-01-02 -0.352480 NaN -1.192319 NaN
2000-01-03 -0.864883 NaN -0.227870 NaN
2000-01-04 NaN -1.222082 NaN -1.233203
2000-01-05 NaN -0.605656 -1.169184 NaN
2000-01-06 NaN -0.948458 NaN -0.684718
2000-01-07 -2.670153 -0.114722 NaN -0.048048
2000-01-08 NaN NaN -0.048788 -0.808838
Метод query() (Экспериментально)
Введено в версии 0.13.
DataFrame имеет метод query(), который позволяет выбирать с помощью выражения.
Вы можете получить значение кадра, где столбец b имеет значения между значениями столбцов a и c. Например:
In [184]: n = 10
In [185]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [186]: df
Out[186]:
a b c
0 0.438921 0.118680 0.863670
1 0.138138 0.577363 0.686602
2 0.595307 0.564592 0.520630
3 0.913052 0.926075 0.616184
4 0.078718 0.854477 0.898725
5 0.076404 0.523211 0.591538
6 0.792342 0.216974 0.564056
7 0.397890 0.454131 0.915716
8 0.074315 0.437913 0.019794
9 0.559209 0.502065 0.026437
# pure python
In [187]: df[(df.a < df.b) & (df.b < df.c)]
Out[187]:
a b c
1 0.138138 0.577363 0.686602
4 0.078718 0.854477 0.898725
5 0.076404 0.523211 0.591538
7 0.397890 0.454131 0.915716
# query
In [188]: df.query('(a < b) & (b < c)')
Out[188]:
a b c
1 0.138138 0.577363 0.686602
4 0.078718 0.854477 0.898725
5 0.076404 0.523211 0.591538
7 0.397890 0.454131 0.915716
Сделайте то же самое, но вернитесь к именованному индексу, если столбец с именем a отсутствует.
In [189]: df = pd.DataFrame(np.random.randint(n / 2, size=(n, 2)), columns=list('bc'))
In [190]: df.index.name = 'a'
In [191]: df
Out[191]:
b c
a
0 0 4
1 0 1
2 3 4
3 4 3
4 1 4
5 0 3
6 0 1
7 3 4
8 2 3
9 1 1
In [192]: df.query('a < b and b < c')
Out[192]:
b c
a
2 3 4
Если вместо этого вы не хотите или не можете назвать свой индекс, вы можете использовать имя index в своём выражении запроса:
In [193]: df = pd.DataFrame(np.random.randint(n, size=(n, 2)), columns=list('bc'))
In [194]: df
Out[194]:
b c
0 3 1
1 3 0
2 5 6
3 5 2
4 7 4
5 0 1
6 2 5
7 0 1
8 6 0
9 7 9
In [195]: df.query('index < b < c')
Out[195]:
b c
2 5 6
Примечание
Если имя вашего индекса совпадает с именем столбца, приоритет отдаётся имени столбца. Например,
In [196]: df = pd.DataFrame({'a': np.random.randint(5, size=5)})
In [197]: df.index.name = 'a'
In [198]: df.query('a > 2') # uses the column 'a', not the index
Out[198]:
a
a
1 3
3 3
Вы по-прежнему можете использовать индекс в выражении запроса, используя специальный идентификатор «index»:
In [199]: df.query('index > 2')
Out[199]:
a
a
3 3
4 2
Если по какой-то причине у вас есть столбец под именем index, вы также можете сослаться на индекс как на ilevel_0, но на этом этапе вы должны подумать о переименовании своих столбцов на что-то менее неоднозначное.
MultiIndex query() Синтаксис
Вы также можете использовать уровни DataFrame с MultiIndex так, как будто они были столбцами в кадре:
In [200]: n = 10
In [201]: colors = np.random.choice(['red', 'green'], size=n)
In [202]: foods = np.random.choice(['eggs', 'ham'], size=n)
In [203]: colors
Out[203]:
array(['red', 'red', 'red', 'green', 'green', 'green', 'green', 'green',
'green', 'green'],
dtype='|S5')
In [204]: foods
Out[204]:
array(['ham', 'ham', 'eggs', 'eggs', 'eggs', 'ham', 'ham', 'eggs', 'eggs',
'eggs'],
dtype='|S4')
In [205]: index = pd.MultiIndex.from_arrays([colors, foods], names=['color', 'food'])
In [206]: df = pd.DataFrame(np.random.randn(n, 2), index=index)
In [207]: df
Out[207]:
0 1
color food
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
green eggs -0.748199 1.318931
eggs -2.029766 0.792652
ham 0.461007 -0.542749
ham -0.305384 -0.479195
eggs 0.095031 -0.270099
eggs -0.707140 -0.773882
eggs 0.229453 0.304418
In [208]: df.query('color == "red"')
Out[208]:
0 1
color food
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
Если уровни MultiIndex не имеют имён, вы можете ссылаться на них, используя специальные имена:
In [209]: df.index.names = [None, None]
In [210]: df
Out[210]:
0 1
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
green eggs -0.748199 1.318931
eggs -2.029766 0.792652
ham 0.461007 -0.542749
ham -0.305384 -0.479195
eggs 0.095031 -0.270099
eggs -0.707140 -0.773882
eggs 0.229453 0.304418
In [211]: df.query('ilevel_0 == "red"')
Out[211]:
0 1
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
Конвенция — это ilevel_0, что означает «уровень индекса 0» для 0-го уровня index.
query() Сценарии использования
Сценарий использования query() — это когда у вас есть набор объектов DataFrame, у которых есть общее подмножество имён столбцов (или уровней/имён индексов). Вы можете передать один и тот же запрос в оба кадра без необходимости указывать, какой кадр вас интересует
In [212]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [213]: df
Out[213]:
a b c
0 0.224283 0.736107 0.139168
1 0.302827 0.657803 0.713897
2 0.611185 0.136624 0.984960
3 0.195246 0.123436 0.627712
4 0.618673 0.371660 0.047902
5 0.480088 0.062993 0.185760
6 0.568018 0.483467 0.445289
7 0.309040 0.274580 0.587101
8 0.258993 0.477769 0.370255
9 0.550459 0.840870 0.304611
In [214]: df2 = pd.DataFrame(np.random.rand(n + 2, 3), columns=df.columns)
In [215]: df2
Out[215]:
a b c
0 0.357579 0.229800 0.596001
1 0.309059 0.957923 0.965663
2 0.123102 0.336914 0.318616
3 0.526506 0.323321 0.860813
4 0.518736 0.486514 0.384724
5 0.190804 0.505723 0.614533
6 0.891939 0.623977 0.676639
7 0.480559 0.378528 0.460858
8 0.420223 0.136404 0.141295
9 0.732206 0.419540 0.604675
10 0.604466 0.848974 0.896165
11 0.589168 0.920046 0.732716
In [216]: expr = '0.0 <= a <= c <= 0.5'
In [217]: map(lambda frame: frame.query(expr), [df, df2])
Out[217]:
[ a b c
8 0.258993 0.477769 0.370255, a b c
2 0.123102 0.336914 0.318616]
query() Сравнение синтаксиса Python и pandas
Полный синтаксис numpy
In [218]: df = pd.DataFrame(np.random.randint(n, size=(n, 3)), columns=list('abc'))
In [219]: df
Out[219]:
a b c
0 7 8 9
1 1 0 7
2 2 7 2
3 6 2 2
4 2 6 3
5 3 8 2
6 1 7 2
7 5 1 5
8 9 8 0
9 1 5 0
In [220]: df.query('(a < b) & (b < c)')
Out[220]:
a b c
0 7 8 9
In [221]: df[(df.a < df.b) & (df.b < df.c)]
Out[221]:
a b c
0 7 8 9
Несколько лучше, так как удаляются скобки (сравнительные операторы привязываются сильнее, чем &/|)
In [222]: df.query('a < b & b < c')
Out[222]:
a b c
0 7 8 9
Используйте английские слова вместо символов
In [223]: df.query('a < b and b < c')
Out[223]:
a b c
0 7 8 9
Довольно близко к тому, как вы можете написать это на бумаге
In [224]: df.query('a < b < c')
Out[224]:
a b c
0 7 8 9
Операторы in и not in
query() также поддерживает специальное использование операторов сравнения Python in и not in, предоставляя лаконичный синтаксис для вызова метода isin объекта Series или DataFrame.
# get all rows where columns "a" and "b" have overlapping values
In [225]: df = pd.DataFrame({'a': list('aabbccddeeff'), 'b': list('aaaabbbbcccc'),
.....: 'c': np.random.randint(5, size=12),
.....: 'd': np.random.randint(9, size=12)})
.....:
In [226]: df
Out[226]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
In [227]: df.query('a in b')
Out[227]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
# How you'd do it in pure Python
In [228]: df[df.a.isin(df.b)]
Out[228]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
In [229]: df.query('a not in b')
Out[229]:
a b c d
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
# pure Python
In [230]: df[~df.a.isin(df.b)]
Out[230]:
a b c d
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
Вы можете объединить это с другими выражениями для очень лаконичных запросов:
# rows where cols a and b have overlapping values and col c's values are less than col d's
In [231]: df.query('a in b and c < d')
Out[231]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
4 c b 3 6
5 c b 0 2
# pure Python
In [232]: df[df.b.isin(df.a) & (df.c < df.d)]
Out[232]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
4 c b 3 6
5 c b 0 2
10 f c 0 6
11 f c 1 2
Примечание
Обратите внимание, что in и not in вычисляются в Python, так как numexpr не имеет эквивалента этой операции. Однако только выражение in/not in само по себе вычисляется в обычном Python. Например, в выражении
df.query('a in b + c + d')
(b + c + d) вычисляется numexpr, а затем операция in вычисляется в обычном Python. В общем случае любые операции, которые можно вычислить с помощью numexpr, будут.
Специальное использование оператора == с объектами list
Сравнение списка значений со столбцом с помощью ==/!= работает аналогично in/not in
In [233]: df.query('b == ["a", "b", "c"]')
Out[233]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
# pure Python
In [234]: df[df.b.isin(["a", "b", "c"])]
Out[234]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
In [235]: df.query('c == [1, 2]')
Out[235]:
a b c d
0 a a 2 6
2 b a 1 6
3 b a 2 1
7 d b 2 1
9 e c 2 0
11 f c 1 2
In [236]: df.query('c != [1, 2]')
Out[236]:
a b c d
1 a a 4 7
4 c b 3 6
5 c b 0 2
6 d b 3 3
8 e c 4 3
10 f c 0 6
# using in/not in
In [237]: df.query('[1, 2] in c')
Out[237]:
a b c d
0 a a 2 6
2 b a 1 6
3 b a 2 1
7 d b 2 1
9 e c 2 0
11 f c 1 2
In [238]: df.query('[1, 2] not in c')
Out[238]:
a b c d
1 a a 4 7
4 c b 3 6
5 c b 0 2
6 d b 3 3
8 e c 4 3
10 f c 0 6
# pure Python
In [239]: df[df.c.isin([1, 2])]
Out[239]:
a b c d
0 a a 2 6
2 b a 1 6
3 b a 2 1
7 d b 2 1
9 e c 2 0
11 f c 1 2
Булевы операторы
Вы можете инвертировать булевы выражения с помощью слова not или оператора ~.
In [240]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [241]: df['bools'] = np.random.rand(len(df)) > 0.5
In [242]: df.query('~bools')
Out[242]:
a b c bools
2 0.697753 0.212799 0.329209 False
7 0.275396 0.691034 0.826619 False
8 0.190649 0.558748 0.262467 False
In [243]: df.query('not bools')
Out[243]:
a b c bools
2 0.697753 0.212799 0.329209 False
7 0.275396 0.691034 0.826619 False
8 0.190649 0.558748 0.262467 False
In [244]: df.query('not bools') == df[~df.bools]
Out[244]:
a b c bools
2 True True True True
7 True True True True
8 True True True True
Конечно, выражения также могут быть сколь угодно сложными
# short query syntax
In [245]: shorter = df.query('a < b < c and (not bools) or bools > 2')
# equivalent in pure Python
In [246]: longer = df[(df.a < df.b) & (df.b < df.c) & (~df.bools) | (df.bools > 2)]
In [247]: shorter
Out[247]:
a b c bools
7 0.275396 0.691034 0.826619 False
In [248]: longer
Out[248]:
a b c bools
7 0.275396 0.691034 0.826619 False
In [249]: shorter == longer
Out[249]:
a b c bools
7 True True True True
Производительность query()
DataFrame.query() с помощью numexpr немного быстрее, чем Python для больших кадров
Примечание
Преимущества использования движка numexpr с DataFrame.query() вы увидите только в том случае, если ваша таблица имеет более примерно 200 000 строк
Этот график был создан с помощью DataFrame с 3 столбцами, каждый из которых содержит значения с плавающей точкой, сгенерированные с помощью numpy.random.randn().
Дубликаты данных
Если вы хотите найти и удалить дублирующиеся строки в DataFrame, есть два метода, которые помогут: duplicated и drop_duplicates. Каждый принимает в качестве аргумента столбцы, которые будут использоваться для определения дублирующих строк.
-
duplicatedвозвращает булевый вектор длиной, равной количеству строк, который указывает, является ли строка дубликатом. -
drop_duplicatesудаляет дублирующие строки.
По умолчанию первой наблюдаемой строкой из набора дубликатов считается уникальной, но каждый метод имеет параметр keep для указания целевых строк, которые нужно сохранить.
-
keep='first'(по умолчанию): отмечать/удалять дубликаты, за исключением первого вхождения. -
keep='last': отмечать/удалять дубликаты, за исключением последнего вхождения. -
keep=False: отмечать/удалять все дубликаты.
In [250]: df2 = pd.DataFrame({'a': ['one', 'one', 'two', 'two', 'two', 'three', 'four'],
.....: 'b': ['x', 'y', 'x', 'y', 'x', 'x', 'x'],
.....: 'c': np.random.randn(7)})
.....:
In [251]: df2
Out[251]:
a b c
0 one x -1.067137
1 one y 0.309500
2 two x -0.211056
3 two y -1.842023
4 two x -0.390820
5 three x -1.964475
6 four x 1.298329
In [252]: df2.duplicated('a')
Out[252]:
0 False
1 True
2 False
3 True
4 True
5 False
6 False
dtype: bool
In [253]: df2.duplicated('a', keep='last')
Out[253]:
0 True
1 False
2 True
3 True
4 False
5 False
6 False
dtype: bool
In [254]: df2.duplicated('a', keep=False)
Out[254]:
0 True
1 True
2 True
3 True
4 True
5 False
6 False
dtype: bool
In [255]: df2.drop_duplicates('a')
Out[255]:
a b c
0 one x -1.067137
2 two x -0.211056
5 three x -1.964475
6 four x 1.298329
In [256]: df2.drop_duplicates('a', keep='last')
Out[256]:
a b c
1 one y 0.309500
4 two x -0.390820
5 three x -1.964475
6 four x 1.298329
In [257]: df2.drop_duplicates('a', keep=False)
Out[257]:
a b c
5 three x -1.964475
6 four x 1.298329
Также вы можете передать список столбцов для определения дубликатов.
In [258]: df2.duplicated(['a', 'b'])
Out[258]:
0 False
1 False
2 False
3 False
4 True
5 False
6 False
dtype: bool
In [259]: df2.drop_duplicates(['a', 'b'])
Out[259]:
a b c
0 one x -1.067137
1 one y 0.309500
2 two x -0.211056
3 two y -1.842023
5 three x -1.964475
6 four x 1.298329
Чтобы удалить дубликаты по значению индекса, используйте Index.duplicated, а затем выполните срез. Те же параметры доступны в параметре keep.
In [260]: df3 = pd.DataFrame({'a': np.arange(6),
.....: 'b': np.random.randn(6)},
.....: index=['a', 'a', 'b', 'c', 'b', 'a'])
.....:
In [261]: df3
Out[261]:
a b
a 0 1.440455
a 1 2.456086
b 2 1.038402
c 3 -0.894409
b 4 0.683536
a 5 3.082764
In [262]: df3.index.duplicated()
Out[262]: array([False, True, False, False, True, True], dtype=bool)
In [263]: df3[~df3.index.duplicated()]
Out[263]:
a b
a 0 1.440455
b 2 1.038402
c 3 -0.894409
In [264]: df3[~df3.index.duplicated(keep='last')]
Out[264]:
a b
c 3 -0.894409
b 4 0.683536
a 5 3.082764
In [265]: df3[~df3.index.duplicated(keep=False)]
Out[265]:
a b
c 3 -0.894409
Метод get(), подобный словарю
У каждого из Series, DataFrame и Panel есть метод get, который может вернуть значение по умолчанию.
In [266]: s = pd.Series([1,2,3], index=['a','b','c'])
In [267]: s.get('a') # equivalent to s['a']
Out[267]: 1
In [268]: s.get('x', default=-1)
Out[268]: -1
Метод select()
Еще один способ извлечения срезов из объекта — это метод select Series, DataFrame и Panel. Этот метод следует использовать только тогда, когда нет более прямого способа. select принимает функцию, которая обрабатывает метки по axis и возвращает булево значение. Например:
In [269]: df.select(lambda x: x == 'A', axis=1)
Out[269]:
A
2000-01-01 0.355794
2000-01-02 1.635763
2000-01-03 0.854409
2000-01-04 -0.216659
2000-01-05 2.414688
2000-01-06 -1.206215
2000-01-07 0.779461
2000-01-08 -0.878999
Метод lookup()
Иногда вам нужно извлечь набор значений по заданной последовательности меток строк и столбцов, и метод lookup позволяет это сделать и возвращает массив NumPy. Например,
In [270]: dflookup = pd.DataFrame(np.random.rand(20,4), columns = ['A','B','C','D']) In [271]: dflookup.lookup(list(range(0,10,2)), ['B','C','A','B','D']) Out[271]: array([ 0.3506, 0.4779, 0.4825, 0.9197, 0.5019])
Объекты Index
Класс pandas Index и его подклассы можно рассматривать как реализующие упорядоченное множество. Дубликаты разрешены. Однако, если вы попытаетесь преобразовать объект Index с дублирующими записями в set, будет выброшено исключение.
Index также предоставляет инфраструктуру, необходимую для поиска, выравнивания данных и повторной индексации. Самый простой способ создать Index напрямую — это передать list или другую последовательность в Index:
In [272]: index = pd.Index(['e', 'd', 'a', 'b']) In [273]: index Out[273]: Index([u'e', u'd', u'a', u'b'], dtype='object') In [274]: 'd' in index Out[274]: True
Вы также можете передать name для хранения в индексе:
In [275]: index = pd.Index(['e', 'd', 'a', 'b'], name='something') In [276]: index.name Out[276]: 'something'
Имя, если оно задано, будет показано в отображении консоли:
In [277]: index = pd.Index(list(range(5)), name='rows') In [278]: columns = pd.Index(['A', 'B', 'C'], name='cols') In [279]: df = pd.DataFrame(np.random.randn(5, 3), index=index, columns=columns) In [280]: df Out[280]: cols A B C rows 0 1.295989 0.185778 0.436259 1 0.678101 0.311369 -0.528378 2 -0.674808 -1.103529 -0.656157 3 1.889957 2.076651 -1.102192 4 -1.211795 -0.791746 0.634724 In [281]: df['A'] Out[281]: rows 0 1.295989 1 0.678101 2 -0.674808 3 1.889957 4 -1.211795 Name: A, dtype: float64
Установка метаданных
Введено в версии 0.13.0.
Индексы «в основном неизменяемы», но можно устанавливать и изменять их метаданные, такие как индекс name (или для MultiIndex, levels и labels).
Вы можете использовать rename, set_names, set_levels и set_labels, чтобы установить эти атрибуты напрямую. По умолчанию они возвращают копию; однако, вы можете указать inplace=True, чтобы данные изменялись на месте.
См. Расширенная индексация для использования MultiIndexes.
In [282]: ind = pd.Index([1, 2, 3])
In [283]: ind.rename("apple")
Out[283]: Int64Index([1, 2, 3], dtype='int64', name=u'apple')
In [284]: ind
Out[284]: Int64Index([1, 2, 3], dtype='int64')
In [285]: ind.set_names(["apple"], inplace=True)
In [286]: ind.name = "bob"
In [287]: ind
Out[287]: Int64Index([1, 2, 3], dtype='int64', name=u'bob')
Введено в версии 0.15.0.
set_names, set_levels и set_labels также принимают необязательный аргумент level`
In [288]: index = pd.MultiIndex.from_product([range(3), ['one', 'two']], names=['first', 'second'])
In [289]: index
Out[289]:
MultiIndex(levels=[[0, 1, 2], [u'one', u'two']],
labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]],
names=[u'first', u'second'])
In [290]: index.levels[1]
Out[290]: Index([u'one', u'two'], dtype='object', name=u'second')
In [291]: index.set_levels(["a", "b"], level=1)
Out[291]:
MultiIndex(levels=[[0, 1, 2], [u'a', u'b']],
labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]],
names=[u'first', u'second'])
Операции над множествами с объектами Index
Предупреждение
В версии 0.15.0 операции над множествами + и - были устаревшими, чтобы предоставить их для операций с численными типами на определенных типах индексов. + можно заменить на .union() или |, а - на .difference().
Две основные операции — union (|), intersection (&). Их можно напрямую вызывать как методы экземпляра или использовать через перегруженные операторы. Разность предоставляется через метод .difference().
In [292]: a = pd.Index(['c', 'b', 'a']) In [293]: b = pd.Index(['c', 'e', 'd']) In [294]: a | b Out[294]: Index([u'a', u'b', u'c', u'd', u'e'], dtype='object') In [295]: a & b Out[295]: Index([u'c'], dtype='object') In [296]: a.difference(b) Out[296]: Index([u'a', u'b'], dtype='object')
Также доступна операция symmetric_difference (^), которая возвращает элементы, присутствующие либо в idx1, либо в idx2, но не в обоих. Это эквивалентно индексу, созданному с помощью idx1.difference(idx2).union(idx2.difference(idx1)), с удаленными дубликатами.
In [297]: idx1 = pd.Index([1, 2, 3, 4]) In [298]: idx2 = pd.Index([2, 3, 4, 5]) In [299]: idx1.symmetric_difference(idx2) Out[299]: Int64Index([1, 5], dtype='int64') In [300]: idx1 ^ idx2 Out[300]: Int64Index([1, 5], dtype='int64')
Пропущенные значения
Введено в версии 0.17.1.
Важно
Хотя Index может содержать пропущенные значения (NaN), этого следует избегать, если вы не хотите получить неожиданные результаты. Например, некоторые операции неявно исключают пропущенные значения.
Index.fillna заполняет пропущенные значения заданным скалярным значением.
In [301]: idx1 = pd.Index([1, np.nan, 3, 4])
In [302]: idx1
Out[302]: Float64Index([1.0, nan, 3.0, 4.0], dtype='float64')
In [303]: idx1.fillna(2)
Out[303]: Float64Index([1.0, 2.0, 3.0, 4.0], dtype='float64')
In [304]: idx2 = pd.DatetimeIndex([pd.Timestamp('2011-01-01'), pd.NaT, pd.Timestamp('2011-01-03')])
In [305]: idx2
Out[305]: DatetimeIndex(['2011-01-01', 'NaT', '2011-01-03'], dtype='datetime64[ns]', freq=None)
In [306]: idx2.fillna(pd.Timestamp('2011-01-02'))
Out[306]: DatetimeIndex(['2011-01-01', '2011-01-02', '2011-01-03'], dtype='datetime64[ns]', freq=None)
Установка/Сброс индекса
Иногда вы загружаете или создаете набор данных в DataFrame и хотите добавить индекс после того, как уже сделали это. Есть несколько способов.
Установка индекса
DataFrame имеет метод set_index, который принимает имя столбца (для обычного Index) или список имен столбцов (для MultiIndex), чтобы создать новый индексированный DataFrame:
In [307]: data
Out[307]:
a b c d
0 bar one z 1.0
1 bar two y 2.0
2 foo one x 3.0
3 foo two w 4.0
In [308]: indexed1 = data.set_index('c')
In [309]: indexed1
Out[309]:
a b d
c
z bar one 1.0
y bar two 2.0
x foo one 3.0
w foo two 4.0
In [310]: indexed2 = data.set_index(['a', 'b'])
In [311]: indexed2
Out[311]:
c d
a b
bar one z 1.0
two y 2.0
foo one x 3.0
two w 4.0
Ключевой параметр append позволяет сохранить существующий индекс и добавить заданные столбцы в MultiIndex:
In [312]: frame = data.set_index('c', drop=False)
In [313]: frame = frame.set_index(['a', 'b'], append=True)
In [314]: frame
Out[314]:
c d
c a b
z bar one z 1.0
y bar two y 2.0
x foo one x 3.0
w foo two w 4.0
Другие параметры в set_index позволяют не удалять столбцы индекса или добавить индекс на месте (без создания нового объекта):
In [315]: data.set_index('c', drop=False)
Out[315]:
a b c d
c
z bar one z 1.0
y bar two y 2.0
x foo one x 3.0
w foo two w 4.0
In [316]: data.set_index(['a', 'b'], inplace=True)
In [317]: data
Out[317]:
c d
a b
bar one z 1.0
two y 2.0
foo one x 3.0
two w 4.0
Сброс индекса
Для удобства существует новая функция в DataFrame под названием reset_index, которая перемещает значения индекса в столбцы DataFrame и устанавливает простой целочисленный индекс. Это обратная операция к set_index
In [318]: data
Out[318]:
c d
a b
bar one z 1.0
two y 2.0
foo one x 3.0
two w 4.0
In [319]: data.reset_index()
Out[319]:
a b c d
0 bar one z 1.0
1 bar two y 2.0
2 foo one x 3.0
3 foo two w 4.0
Вывод более похож на таблицу SQL или массив записей. Имена столбцов, полученные из индекса, — это имена, хранящиеся в атрибуте names.
Вы можете использовать ключевое слово level, чтобы удалить только часть индекса:
In [320]: frame
Out[320]:
c d
c a b
z bar one z 1.0
y bar two y 2.0
x foo one x 3.0
w foo two w 4.0
In [321]: frame.reset_index(level=1)
Out[321]:
a c d
c b
z one bar z 1.0
y two bar y 2.0
x one foo x 3.0
w two foo w 4.0
reset_index принимает необязательный параметр drop, который, если он равен true, просто отбрасывает индекс, а не помещает значения индекса в столбцы DataFrame.
Примечание
Метод reset_index раньше назывался delevel, который теперь устарел.
Добавление произвольного индекса
Если вы создаете индекс самостоятельно, вы можете просто назначить его полю index:
data.index = index
Возврат представления или копии
При установке значений в объекте pandas необходимо учитывать, чтобы избежать того, что называется chained indexing. Вот пример.
In [322]: dfmi = pd.DataFrame([list('abcd'),
.....: list('efgh'),
.....: list('ijkl'),
.....: list('mnop')],
.....: columns=pd.MultiIndex.from_product([['one','two'],
.....: ['first','second']]))
.....:
In [323]: dfmi
Out[323]:
one two
first second first second
0 a b c d
1 e f g h
2 i j k l
3 m n o p
Сравните эти два метода доступа:
In [324]: dfmi['one']['second'] Out[324]: 0 b 1 f 2 j 3 n Name: second, dtype: object
In [325]: dfmi.loc[:,('one','second')]
Out[325]:
0 b
1 f
2 j
3 n
Name: (one, second), dtype: object
Они оба дают одинаковые результаты, так какой же следует использовать? Полезно понять порядок операций с ними и почему метод 2 (.loc) намного предпочтительнее метода 1 (цепочечная []).
dfmi['one'] выбирает первый уровень столбцов и возвращает DataFrame, который имеет одноуровневый индекс. Затем другая операция Python dfmi_with_one['second'] выбирает ряд, индексированный по 'second'. Это показано переменной dfmi_with_one, потому что pandas рассматривает эти операции как отдельные события. Например, отдельные вызовы __getitem__, поэтому он должен рассматривать их как линейные операции, которые происходят одна за другой.
В отличие от df.loc[:,('one','second')], который передает вложенную кортеж из (slice(None),('one','second')) в один вызов __getitem__. Это позволяет pandas обрабатывать это как единое целое. Кроме того, такой порядок операций может быть значительно быстрее и позволяет индексировать обе оси, если это необходимо.
Почему присваивание не работает при использовании цепочечной индексации?
Проблема в предыдущем разделе — просто вопрос производительности. Что случилось с предупреждением SettingWithCopy? Мы обычно не выводим предупреждения, когда вы делаете что-то, что может стоить несколько дополнительных миллисекунд!
Но оказывается, что присвоение результату цепочечной индексации имеет неопределенные результаты. Чтобы увидеть это, подумайте о том, как интерпретатор Python выполняет этот код:
dfmi.loc[:,('one','second')] = value
# becomes
dfmi.loc.__setitem__((slice(None), ('one', 'second')), value)
Но этот код обрабатывается по-другому:
dfmi['one']['second'] = value
# becomes
dfmi.__getitem__('one').__setitem__('second', value)
Видите __getitem__ там? За пределами простых случаев очень сложно предсказать, вернет ли он представление или копию (это зависит от расположения массива в памяти, в отношении которого pandas не дает никаких гарантий), и, следовательно, изменит ли __setitem__ dfmi или временный объект, который выбрасывается сразу же после этого. Это то, о чем предупреждает SettingWithCopy!
Примечание
Возможно, вы задаётесь вопросом, стоит ли беспокоиться о свойстве loc в первом примере. Но dfmi.loc гарантированно является dfmi с изменённым поведением индексирования, поэтому dfmi.loc.__getitem__ / dfmi.loc.__setitem__ работают непосредственно с dfmi. Конечно, dfmi.loc.__getitem__(idx) может быть представлением или копией dfmi.
Иногда возникает предупреждение SettingWithCopy в случаях, когда нет очевидного цепочечного индексирования. Эти ошибки — те, которые SettingWithCopy предназначен для обнаружения! Pandas, вероятно, пытается предупредить вас о том, что вы сделали это:
def do_something(df): foo = df[['bar', 'baz']] # Is foo a view? A copy? Nobody knows! # ... many lines here ... foo['quux'] = value # We don't know whether this will modify df or not! return foo
Упс!
Порядок вычислений имеет значение
Кроме того, в цепочечных выражениях порядок может определить, возвращается ли копия или нет. Если выражение будет устанавливать значения в копии среза, будет возбуждено исключение SettingWithCopy (это поведение подъёма/предупреждения введено начиная с 0.13.0).
Вы можете контролировать действие цепочечной присваивания с помощью опции mode.chained_assignment, которая может принимать значения ['raise','warn',None], где показ предупреждения является значением по умолчанию.
In [326]: dfb = pd.DataFrame({'a' : ['one', 'one', 'two',
.....: 'three', 'two', 'one', 'six'],
.....: 'c' : np.arange(7)})
.....:
# This will show the SettingWithCopyWarning
# but the frame values will be set
In [327]: dfb['c'][dfb.a.str.startswith('o')] = 42
Однако это действует на копию и не сработает.
>>> pd.set_option('mode.chained_assignment','warn')
>>> dfb[dfb.a.str.startswith('o')]['c'] = 42
Traceback (most recent call last)
...
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
Цепочечная присваивания также может возникнуть при настройке в фрейме смешанного типа.
Примечание
Эти правила настройки применяются ко всем .loc/.iloc/.ix
Это правильный метод доступа
In [328]: dfc = pd.DataFrame({'A':['aaa','bbb','ccc'],'B':[1,2,3]})
In [329]: dfc.loc[0,'A'] = 11
In [330]: dfc
Out[330]:
A B
0 11 1
1 bbb 2
2 ccc 3
Это может сработать в некоторых случаях, но не гарантируется, и поэтому следует избегать.
In [331]: dfc = dfc.copy()
In [332]: dfc['A'][0] = 111
In [333]: dfc
Out[333]:
A B
0 111 1
1 bbb 2
2 ccc 3
Это не сработает вообще и поэтому следует избегать.
>>> pd.set_option('mode.chained_assignment','raise')
>>> dfc.loc[0]['A'] = 1111
Traceback (most recent call last)
...
SettingWithCopyException:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
Предупреждение
Предупреждения/исключения при цепочечной присваивания предназначены для информирования пользователя о возможном некорректном присваивании. Могут быть ложноположительные срабатывания; ситуации, когда цепочечная присваивания ошибочно сообщается.
© 2008–2012, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/0.19.2/indexing.html