Индексирование и выбор данных
Информация о маркировке осей в объектах 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
Это может быть полезно для применения преобразования (на месте) к подмножеству столбцов.
Доступ к атрибутам
Вы можете получить доступ к индексу в Series, столбцу в DataFrame, и элементу в Panel непосредственно как к атрибуту:
In [12]: sa = pd.Series([1,2,3],index=list('abc'))
In [13]: dfa = df.copy()
In [14]: sa.b
Out[14]: 2
In [15]: dfa.A
Out[15]:
2000-01-01 -0.282863
2000-01-02 -0.173215
2000-01-03 -2.104569
2000-01-04 -0.706771
2000-01-05 0.567020
2000-01-06 0.113648
2000-01-07 0.577046
2000-01-08 -1.157892
Freq: D, Name: A, dtype: float64
In [16]: panel.one
Out[16]:
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 [17]: sa.a = 5
In [18]: sa
Out[18]:
a 5
b 2
c 3
dtype: int64
In [19]: dfa.A = list(range(len(dfa.index))) # ok if A already exists
In [20]: dfa
Out[20]:
A B C D
2000-01-01 0 0.469112 -1.509059 -1.135632
2000-01-02 1 1.212112 0.119209 -1.044236
2000-01-03 2 -0.861849 -0.494929 1.071804
2000-01-04 3 0.721555 -1.039575 0.271860
2000-01-05 4 -0.424972 0.276232 -1.087401
2000-01-06 5 -0.673690 -1.478427 0.524988
2000-01-07 6 0.404705 -1.715002 -1.039268
2000-01-08 7 -0.370647 -1.344312 0.844885
In [21]: dfa['A'] = list(range(len(dfa.index))) # use this form to create a new column
In [22]: dfa
Out[22]:
A B C D
2000-01-01 0 0.469112 -1.509059 -1.135632
2000-01-02 1 1.212112 0.119209 -1.044236
2000-01-03 2 -0.861849 -0.494929 1.071804
2000-01-04 3 0.721555 -1.039575 0.271860
2000-01-05 4 -0.424972 0.276232 -1.087401
2000-01-06 5 -0.673690 -1.478427 0.524988
2000-01-07 6 0.404705 -1.715002 -1.039268
2000-01-08 7 -0.370647 -1.344312 0.844885
Предупреждение
- Вы можете использовать этот доступ только если элемент индекса является допустимым идентификатором 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 [23]: x = pd.DataFrame({'x': [1, 2, 3], 'y': [3, 4, 5]})
In [24]: x.iloc[1] = dict(x=9, y=99)
In [25]: x
Out[25]:
x y
0 1 3
1 9 99
2 3 5
Срезы диапазонов
Самый надежный и последовательный способ нарезки диапазонов по произвольным осям описан в разделе Выбор по позиции, описывающем метод .iloc. На данный момент мы объясняем семантику срезов с использованием оператора [].
В случае с Series синтаксис работает точно так же, как и с ndarray, возвращая срез значений и соответствующих меток:
In [26]: s[:5] Out[26]: 2000-01-01 -0.282863 2000-01-02 -0.173215 2000-01-03 -2.104569 2000-01-04 -0.706771 2000-01-05 0.567020 Freq: D, Name: A, dtype: float64 In [27]: s[::2] Out[27]: 2000-01-01 -0.282863 2000-01-03 -2.104569 2000-01-05 0.567020 2000-01-07 0.577046 Freq: 2D, Name: A, dtype: float64 In [28]: s[::-1] Out[28]: 2000-01-08 -1.157892 2000-01-07 0.577046 2000-01-06 0.113648 2000-01-05 0.567020 2000-01-04 -0.706771 2000-01-03 -2.104569 2000-01-02 -0.173215 2000-01-01 -0.282863 Freq: -1D, Name: A, dtype: float64
Обратите внимание, что настройка также работает:
In [29]: s2 = s.copy() In [30]: s2[:5] = 0 In [31]: s2 Out[31]: 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.113648 2000-01-07 0.577046 2000-01-08 -1.157892 Freq: D, Name: A, dtype: float64
В случае с DataFrame нарезка внутри [] нарезает строки. Это обеспечивается в основном как удобство, так как это настолько распространенная операция.
In [32]: df[:3]
Out[32]:
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
In [33]: df[::-1]
Out[33]:
A B C D
2000-01-08 -1.157892 -0.370647 -1.344312 0.844885
2000-01-07 0.577046 0.404705 -1.715002 -1.039268
2000-01-06 0.113648 -0.673690 -1.478427 0.524988
2000-01-05 0.567020 -0.424972 0.276232 -1.087401
2000-01-04 -0.706771 0.721555 -1.039575 0.271860
2000-01-03 -2.104569 -0.861849 -0.494929 1.071804
2000-01-02 -0.173215 1.212112 0.119209 -1.044236
2000-01-01 -0.282863 0.469112 -1.509059 -1.135632
Выбор по метке
Предупреждение
Возвращаемое значение — копия или ссылка при операции установки, может зависеть от контекста. Иногда это называется chained assignment и следует избегать. См. Возвращение Вида или Копии
Предупреждение
.loc строго, когда вы предоставляете слайсеры, которые несовместимы (или не преобразуемы) с типом индекса. Например, использование целых чисел в DatetimeIndex. Это вызовет TypeError. In [34]: dfl = pd.DataFrame(np.random.randn(5,4), columns=list('ABCD'), index=pd.date_range('20130101',periods=5))
In [35]: dfl
Out[35]:
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 [36]: dfl.loc['20130102':'20130104']
Out[36]:
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 [37]: s1 = pd.Series(np.random.randn(6),index=list('abcdef'))
In [38]: s1
Out[38]:
a 1.431256
b 1.340309
c -1.170299
d -0.226169
e 0.410835
f 0.813850
dtype: float64
In [39]: s1.loc['c':]
Out[39]:
c -1.170299
d -0.226169
e 0.410835
f 0.813850
dtype: float64
In [40]: s1.loc['b']
Out[40]: 1.3403088497993827
Обратите внимание, что установка также работает:
In [41]: s1.loc['c':] = 0 In [42]: s1 Out[42]: a 1.431256 b 1.340309 c 0.000000 d 0.000000 e 0.000000 f 0.000000 dtype: float64
С DataFrame
In [43]: df1 = pd.DataFrame(np.random.randn(6,4),
....: index=list('abcdef'),
....: columns=list('ABCD'))
....:
In [44]: df1
Out[44]:
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 [45]: df1.loc[['a', 'b', 'd'], :]
Out[45]:
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 [46]: df1.loc['d':, 'A':'C']
Out[46]:
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 [47]: df1.loc['a'] Out[47]: A 0.132003 B -0.827317 C -0.076467 D -1.187678 Name: a, dtype: float64
Для получения значений с помощью массива булевых значений
In [48]: df1.loc['a'] > 0
Out[48]:
A True
B False
C False
D False
Name: a, dtype: bool
In [49]: df1.loc[:, df1.loc['a'] > 0]
Out[49]:
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 [50]: df1.loc['a', 'A'] Out[50]: 0.13200317033032932
Выбор по Позиции
Предупреждение
Возвращаемое значение — копия или ссылка при операции установки, может зависеть от контекста. Иногда это называется chained assignment и следует избегать. См. Возвращение Вида или Копии
pandas предоставляет набор методов для получения индексирования только по целым числам. Семантика тесно следует зарезенным python и numpy. Это 0-based индексирование. При нарезке начальная граница включается, а верхняя граница исключается. Попытка использовать нецелое число, даже допустимую метку, вызовет IndexError.
Атрибут .iloc — основной метод доступа. Допустимые входные значения:
- Целое число, например
5 - Список или массив целых чисел
[4, 3, 0] - Объект среза с целыми числами
1:7 - Массив булевых значений
- Функция
callable, см. Выбор по Функции
In [51]: s1 = pd.Series(np.random.randn(5), index=list(range(0,10,2))) In [52]: s1 Out[52]: 0 0.695775 2 0.341734 4 0.959726 6 -1.110336 8 -0.619976 dtype: float64 In [53]: s1.iloc[:3] Out[53]: 0 0.695775 2 0.341734 4 0.959726 dtype: float64 In [54]: s1.iloc[3] Out[54]: -1.1103361028911669
Обратите внимание, что установка также работает:
In [55]: s1.iloc[:3] = 0 In [56]: s1 Out[56]: 0 0.000000 2 0.000000 4 0.000000 6 -1.110336 8 -0.619976 dtype: float64
С DataFrame
In [57]: df1 = pd.DataFrame(np.random.randn(6,4),
....: index=list(range(0,12,2)),
....: columns=list(range(0,8,2)))
....:
In [58]: df1
Out[58]:
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 [59]: df1.iloc[:3]
Out[59]:
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 [60]: df1.iloc[1:5, 2:4]
Out[60]:
4 6
2 0.301624 -2.179861
4 1.462696 -1.743161
6 1.314232 0.690579
8 0.014871 3.357427
Выбор по целочисленному списку
In [61]: df1.iloc[[1, 3, 5], [1, 3]]
Out[61]:
2 6
2 -0.154951 -2.179861
6 -0.345352 0.690579
10 -1.236269 -0.487602
In [62]: df1.iloc[1:3, :]
Out[62]:
0 2 4 6
2 0.403310 -0.154951 0.301624 -2.179861
4 -1.369849 -0.954208 1.462696 -1.743161
In [63]: df1.iloc[:, 1:3]
Out[63]:
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 [64]: df1.iloc[1, 1] Out[64]: -0.15495077442490321
Для получения поперечного сечения по целочисленной позиции (эквивалентно df.xs(1))
In [65]: df1.iloc[1] Out[65]: 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 [66]: x = list('abcdef')
In [67]: x
Out[67]: ['a', 'b', 'c', 'd', 'e', 'f']
In [68]: x[4:10]
Out[68]: ['e', 'f']
In [69]: x[8:10]
Out[69]: []
In [70]: s = pd.Series(x)
In [71]: s
Out[71]:
0 a
1 b
2 c
3 d
4 e
5 f
dtype: object
In [72]: s.iloc[4:10]
Out[72]:
4 e
5 f
dtype: object
In [73]: s.iloc[8:10]
Out[73]: Series([], dtype: object)
Примечание
До версии v0.14.0, iloc не принимали индексы вне диапазона для срезов, например, значение, которое превышает длину индексируемого объекта.
Обратите внимание, что это может привести к пустой оси (например, возвращается пустой DataFrame).
In [74]: dfl = pd.DataFrame(np.random.randn(5,2), columns=list('AB'))
In [75]: dfl
Out[75]:
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 [76]: dfl.iloc[:, 2:3]
Out[76]:
Empty DataFrame
Columns: []
Index: [0, 1, 2, 3, 4]
In [77]: dfl.iloc[:, 1:3]
Out[77]:
B
0 -2.182937
1 0.084844
2 1.519970
3 0.600178
4 0.132885
In [78]: dfl.iloc[4:6]
Out[78]:
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 [79]: df1 = pd.DataFrame(np.random.randn(6, 4),
....: index=list('abcdef'),
....: columns=list('ABCD'))
....:
In [80]: df1
Out[80]:
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 [81]: df1.loc[lambda df: df.A > 0, :]
Out[81]:
A B C D
c 0.299368 -0.863838 0.408204 -1.048089
e 1.289997 0.082423 -0.055758 0.536580
In [82]: df1.loc[:, lambda df: ['A', 'B']]
Out[82]:
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 [83]: df1.iloc[:, lambda df: [0, 1]]
Out[83]:
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 [84]: df1[lambda df: df.columns[0]]
Out[84]:
a -0.023688
b -0.251905
c 0.299368
d -0.025747
e 1.289997
f -0.489682
Name: A, dtype: float64
Вы можете использовать индексирование по функции в Series.
In [85]: df1.A.loc[lambda s: s > 0] Out[85]: c 0.299368 e 1.289997 Name: A, dtype: float64
Используя эти методы/индексаторы, вы можете объединять операции выбора данных без использования временных переменных.
In [86]: bb = pd.read_csv('data/baseball.csv', index_col='id')
In [87]: (bb.groupby(['year', 'team']).sum()
....: .loc[lambda df: df.r > 100])
....:
Out[87]:
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 [88]: s = pd.Series([0,1,2,3,4,5]) # When no arguments are passed, returns 1 row. In [89]: s.sample() Out[89]: 3 3 dtype: int64 # One may specify either a number of rows: In [90]: s.sample(n=3) Out[90]: 0 0 2 2 1 1 dtype: int64 # Or a fraction of the rows: In [91]: s.sample(frac=0.5) Out[91]: 4 4 2 2 3 3 dtype: int64
По умолчанию sample вернёт каждую строку не более одного раза, но можно выбрать с заменой с помощью параметра replace.
In [92]: s = pd.Series([0,1,2,3,4,5]) # Without replacement (default): In [93]: s.sample(n=6, replace=False) Out[93]: 4 4 0 0 3 3 5 5 2 2 1 1 dtype: int64 # With replacement: In [94]: s.sample(n=6, replace=True) Out[94]: 4 4 2 2 4 4 3 3 0 0 1 1 dtype: int64
По умолчанию у каждой строки одинаковая вероятность выбора, но если вы хотите, чтобы у строк были разные вероятности, вы можете передать функцию sample взвешивания выборок как weights. Эти веса могут быть списком, массивом numpy или Series, но они должны иметь такую же длину, как и объект, который вы выбираете. Пропущенные значения будут рассматриваться как вес 0, а значения inf недопустимы. Если суммы весов не равны 1, они будут перенормированы путём деления всех весов на сумму весов. Например:
In [95]: s = pd.Series([0,1,2,3,4,5]) In [96]: example_weights = [0, 0, 0.2, 0.2, 0.2, 0.4] In [97]: s.sample(n=3, weights=example_weights) Out[97]: 5 5 4 4 2 2 dtype: int64 # Weights will be re-normalized automatically In [98]: example_weights2 = [0.5, 0, 0, 0, 0, 0] In [99]: s.sample(n=1, weights=example_weights2) Out[99]: 0 0 dtype: int64
При применении к DataFrame вы можете использовать столбец DataFrame в качестве весов для выборки (при условии, что вы выбираете строки, а не столбцы), просто передав имя столбца как строку.
In [100]: df2 = pd.DataFrame({'col1':[9,8,7,6], 'weight_column':[0.5, 0.4, 0.1, 0]})
In [101]: df2.sample(n = 3, weights = 'weight_column')
Out[101]:
col1 weight_column
1 8 0.4
2 7 0.1
0 9 0.5
sample также позволяет пользователям выбирать столбцы вместо строк с помощью аргумента axis.
In [102]: df3 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})
In [103]: df3.sample(n=1, axis=1)
Out[103]:
col1
0 1
1 2
2 3
Наконец, можно установить начальное значение для генератора случайных чисел sample с помощью аргумента random_state, который примет либо целое число (в качестве начального значения), либо объект numpy RandomState.
In [104]: df4 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})
# With a given seed, the sample will always draw the same rows.
In [105]: df4.sample(n=2, random_state=2)
Out[105]:
col1 col2
2 3 4
1 2 3
In [106]: df4.sample(n=2, random_state=2)
Out[106]:
col1 col2
2 3 4
1 2 3
Установка с Увеличением
Новое в версии 0.13.
Операции .loc/.ix/[] могут выполнять расширение при установке несуществующего ключа для этой оси.
В случае Series это фактически операция добавления
In [107]: se = pd.Series([1,2,3]) In [108]: se Out[108]: 0 1 1 2 2 3 dtype: int64 In [109]: se[5] = 5. In [110]: se Out[110]: 0 1.0 1 2.0 2 3.0 5 5.0 dtype: float64
DataFrame может быть увеличена на любой оси с помощью .loc
In [111]: dfi = pd.DataFrame(np.arange(6).reshape(3,2), .....: columns=['A','B']) .....: In [112]: dfi Out[112]: A B 0 0 1 1 2 3 2 4 5 In [113]: dfi.loc[:,'C'] = dfi.loc[:,'A'] In [114]: dfi Out[114]: A B C 0 0 1 0 1 2 3 2 2 4 5 4
Это похоже на операцию append на DataFrame.
In [115]: dfi.loc[3] = 5 In [116]: dfi Out[116]: 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 [117]: s.iat[5] Out[117]: 5 In [118]: df.at[dates[5], 'A'] Out[118]: 0.1136484096888855 In [119]: df.iat[3, 0] Out[119]: -0.70677113363008448
Вы также можете установить значения с помощью этих же индексаторов.
In [120]: df.at[dates[5], 'E'] = 7 In [121]: df.iat[3, 0] = 7
at может увеличить объект на месте, как описано выше, если индексатор отсутствует.
In [122]: df.at[dates[-1]+1, 0] = 7
In [123]: df
Out[123]:
A B C D E 0
2000-01-01 -0.282863 0.469112 -1.509059 -1.135632 NaN NaN
2000-01-02 -0.173215 1.212112 0.119209 -1.044236 NaN NaN
2000-01-03 -2.104569 -0.861849 -0.494929 1.071804 NaN NaN
2000-01-04 7.000000 0.721555 -1.039575 0.271860 NaN NaN
2000-01-05 0.567020 -0.424972 0.276232 -1.087401 NaN NaN
2000-01-06 0.113648 -0.673690 -1.478427 0.524988 7.0 NaN
2000-01-07 0.577046 0.404705 -1.715002 -1.039268 NaN NaN
2000-01-08 -1.157892 -0.370647 -1.344312 0.844885 NaN NaN
2000-01-09 NaN NaN NaN NaN NaN 7.0
Булево индексирование
Ещё одной распространённой операцией является использование булевых векторов для фильтрации данных. Операторы: | для or, & для and, и ~ для not. Они обязательно должны быть сгруппированы с помощью скобок.
Использование булевого вектора для индексирования Series работает точно так же, как в numpy ndarray:
In [124]: s = pd.Series(range(-3, 4)) In [125]: s Out[125]: 0 -3 1 -2 2 -1 3 0 4 1 5 2 6 3 dtype: int64 In [126]: s[s > 0] Out[126]: 4 1 5 2 6 3 dtype: int64 In [127]: s[(s < -1) | (s > 0.5)] Out[127]: 0 -3 1 -2 4 1 5 2 6 3 dtype: int64 In [128]: s[~(s < 0)] Out[128]: 3 0 4 1 5 2 6 3 dtype: int64
Вы можете выбрать строки из DataFrame, используя булевой вектор, длина которого равна длине индекса DataFrame (например, что-то полученное из одного из столбцов DataFrame):
In [129]: df[df['A'] > 0]
Out[129]:
A B C D E 0
2000-01-04 7.000000 0.721555 -1.039575 0.271860 NaN NaN
2000-01-05 0.567020 -0.424972 0.276232 -1.087401 NaN NaN
2000-01-06 0.113648 -0.673690 -1.478427 0.524988 7.0 NaN
2000-01-07 0.577046 0.404705 -1.715002 -1.039268 NaN NaN
Список понимания и метод map Series также могут использоваться для создания более сложных критериев:
In [130]: 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 [131]: criterion = df2['a'].map(lambda x: x.startswith('t'))
In [132]: df2[criterion]
Out[132]:
a b c
2 two y 0.109121
3 three x 1.126203
4 two y -0.977349
# equivalent but slower
In [133]: df2[[x.startswith('t') for x in df2['a']]]
Out[133]:
a b c
2 two y 0.109121
3 three x 1.126203
4 two y -0.977349
# Multiple criteria
In [134]: df2[criterion & (df2['b'] == 'x')]
Out[134]:
a b c
3 three x 1.126203
Обратите внимание, что с помощью методов выбора Выбор по Меткам, Выбор по Позиции и Расширенное Индексирование вы можете выбирать по нескольким осям, используя булевые векторы в сочетании с другими выражениями индексирования.
In [135]: df2.loc[criterion & (df2['b'] == 'x'),'b':'c'] Out[135]: b c 3 x 1.126203
Индексирование с isin
Рассмотрите метод isin Series, который возвращает булевой вектор, равный true везде, где элементы Series присутствуют в переданном списке. Это позволяет вам выбрать строки, где один или несколько столбцов имеют значения, которые вы хотите:
In [136]: s = pd.Series(np.arange(5), index=np.arange(5)[::-1], dtype='int64') In [137]: s Out[137]: 4 0 3 1 2 2 1 3 0 4 dtype: int64 In [138]: s.isin([2, 4, 6]) Out[138]: 4 False 3 False 2 True 1 False 0 True dtype: bool In [139]: s[s.isin([2, 4, 6])] Out[139]: 2 2 0 4 dtype: int64
Тот же метод доступен для Index объектов и полезен в тех случаях, когда вы не знаете, какие из искомых меток на самом деле присутствуют:
In [140]: s[s.index.isin([2, 4, 6])] Out[140]: 4 0 2 2 dtype: int64 # compare it to the following In [141]: s[[2, 4, 6]] Out[141]: 2 2.0 4 0.0 6 NaN dtype: float64
Кроме того, MultiIndex позволяет выбрать отдельный уровень для использования в проверке на членство:
In [142]: s_mi = pd.Series(np.arange(6), .....: index=pd.MultiIndex.from_product([[0, 1], ['a', 'b', 'c']])) .....: In [143]: s_mi Out[143]: 0 a 0 b 1 c 2 1 a 3 b 4 c 5 dtype: int64 In [144]: s_mi.iloc[s_mi.index.isin([(1, 'a'), (2, 'b'), (0, 'c')])] Out[144]: 0 c 2 1 a 3 dtype: int64 In [145]: s_mi.iloc[s_mi.index.isin(['a', 'c', 'e'], level=1)] Out[145]: 0 a 0 c 2 1 a 3 c 5 dtype: int64
DataFrame также имеет метод isin. При вызове isin, передайте набор значений в виде массива или словаря. Если values — массив, isin возвращает DataFrame булевых значений, имеющий такую же форму, как и исходный DataFrame, со значением True везде, где элемент присутствует в последовательности значений.
In [146]: df = pd.DataFrame({'vals': [1, 2, 3, 4], 'ids': ['a', 'b', 'f', 'n'],
.....: 'ids2': ['a', 'n', 'c', 'n']})
.....:
In [147]: values = ['a', 'b', 1, 3]
In [148]: df.isin(values)
Out[148]:
ids ids2 vals
0 True True True
1 True False False
2 False False True
3 False False False
Часто вам нужно сопоставить определённые значения с определёнными столбцами. Просто сделайте values словарем, где ключ — столбец, а значение — список элементов, которые вы хотите проверить.
dict Сочетайте DataFrame’s isin с методами any() и all(), чтобы быстро выбрать подмножества данных, которые соответствуют заданным критериям. Для выбора строки, где каждый столбец соответствует своему критерию:
In [151]: values = {'ids': ['a', 'b'], 'ids2': ['a', 'c'], 'vals': [1, 3]}
In [152]: row_mask = df.isin(values).all(1)
In [153]: df[row_mask]
Out[153]:
ids ids2 vals
0 a a 1
Метод where() и Маскирование
Выбор значений из Series с помощью булевого вектора обычно возвращает подмножество данных. Чтобы гарантировать, что результат выбора имеет ту же форму, что и исходные данные, можно использовать метод where в Series и DataFrame.
Для возвращения только выбранных строк
In [154]: s[s > 0] Out[154]: 3 1 2 2 1 3 0 4 dtype: int64
Для возвращения Series с той же формой, что и исходная
In [155]: s.where(s > 0) Out[155]: 4 NaN 3 1.0 2 2.0 1 3.0 0 4.0 dtype: float64
Выбор значений из DataFrame с булевым критерием теперь также сохраняет форму входных данных. where используется под капотом в качестве реализации. Эквивалентно df.where(df < 0)
In [156]: df[df < 0]
Out[156]:
A B C D
2000-01-01 -1.282782 NaN -1.071357 NaN
2000-01-02 NaN NaN NaN -0.744471
2000-01-03 NaN NaN -0.964980 -0.845696
2000-01-04 -1.340896 NaN -1.328865 NaN
2000-01-05 -1.717693 NaN NaN NaN
2000-01-06 NaN NaN -1.197071 -1.066969
2000-01-07 -0.303421 -0.858447 NaN -0.028665
2000-01-08 NaN NaN NaN NaN
Кроме того, where принимает необязательный аргумент other для замены значений, где условие равно False, в возвращаемой копии.
In [157]: df.where(df < 0, -df)
Out[157]:
A B C D
2000-01-01 -1.282782 -0.781836 -1.071357 -0.441153
2000-01-02 -2.353925 -0.583787 -0.221471 -0.744471
2000-01-03 -0.758527 -1.729689 -0.964980 -0.845696
2000-01-04 -1.340896 -1.846883 -1.328865 -1.682706
2000-01-05 -1.717693 -0.888782 -0.228440 -0.901805
2000-01-06 -1.171216 -0.520260 -1.197071 -1.066969
2000-01-07 -0.303421 -0.858447 -0.306996 -0.028665
2000-01-08 -0.384316 -1.574159 -1.588931 -0.476720
Вам может потребоваться установить значения на основе некоторых булевых критериев. Это можно сделать интуитивно следующим образом:
In [158]: s2 = s.copy()
In [159]: s2[s2 < 0] = 0
In [160]: s2
Out[160]:
4 0
3 1
2 2
1 3
0 4
dtype: int64
In [161]: df2 = df.copy()
In [162]: df2[df2 < 0] = 0
In [163]: df2
Out[163]:
A B C D
2000-01-01 0.000000 0.781836 0.000000 0.441153
2000-01-02 2.353925 0.583787 0.221471 0.000000
2000-01-03 0.758527 1.729689 0.000000 0.000000
2000-01-04 0.000000 1.846883 0.000000 1.682706
2000-01-05 0.000000 0.888782 0.228440 0.901805
2000-01-06 1.171216 0.520260 0.000000 0.000000
2000-01-07 0.000000 0.000000 0.306996 0.000000
2000-01-08 0.384316 1.574159 1.588931 0.476720
По умолчанию where возвращает изменённую копию данных. Есть необязательный параметр inplace, чтобы исходные данные можно было изменить без создания копии:
In [164]: df_orig = df.copy()
In [165]: df_orig.where(df > 0, -df, inplace=True);
In [166]: df_orig
Out[166]:
A B C D
2000-01-01 1.282782 0.781836 1.071357 0.441153
2000-01-02 2.353925 0.583787 0.221471 0.744471
2000-01-03 0.758527 1.729689 0.964980 0.845696
2000-01-04 1.340896 1.846883 1.328865 1.682706
2000-01-05 1.717693 0.888782 0.228440 0.901805
2000-01-06 1.171216 0.520260 1.197071 1.066969
2000-01-07 0.303421 0.858447 0.306996 0.028665
2000-01-08 0.384316 1.574159 1.588931 0.476720
выравнивание
Кроме того, where выравнивает входное булевое условие (ndarray или DataFrame) таким образом, чтобы была возможна частичная выборка с установкой. Это аналогично частичной установке с помощью .ix (но для содержимого, а не для меток осей)
In [167]: df2 = df.copy()
In [168]: df2[ df2[1:4] > 0 ] = 3
In [169]: df2
Out[169]:
A B C D
2000-01-01 -1.282782 0.781836 -1.071357 0.441153
2000-01-02 3.000000 3.000000 3.000000 -0.744471
2000-01-03 3.000000 3.000000 -0.964980 -0.845696
2000-01-04 -1.340896 3.000000 -1.328865 3.000000
2000-01-05 -1.717693 0.888782 0.228440 0.901805
2000-01-06 1.171216 0.520260 -1.197071 -1.066969
2000-01-07 -0.303421 -0.858447 0.306996 -0.028665
2000-01-08 0.384316 1.574159 1.588931 0.476720
Введено в версии 0.13.
Где также можно принять axis и level параметры для выравнивания входных данных при выполнении where.
In [170]: df2 = df.copy()
In [171]: df2.where(df2>0,df2['A'],axis='index')
Out[171]:
A B C D
2000-01-01 -1.282782 0.781836 -1.282782 0.441153
2000-01-02 2.353925 0.583787 0.221471 2.353925
2000-01-03 0.758527 1.729689 0.758527 0.758527
2000-01-04 -1.340896 1.846883 -1.340896 1.682706
2000-01-05 -1.717693 0.888782 0.228440 0.901805
2000-01-06 1.171216 0.520260 1.171216 1.171216
2000-01-07 -0.303421 -0.303421 0.306996 -0.303421
2000-01-08 0.384316 1.574159 1.588931 0.476720
Это эквивалентно (но быстрее, чем) следующее.
In [172]: df2 = df.copy()
In [173]: df.apply(lambda x, y: x.where(x>0,y), y=df['A'])
Out[173]:
A B C D
2000-01-01 -1.282782 0.781836 -1.282782 0.441153
2000-01-02 2.353925 0.583787 0.221471 2.353925
2000-01-03 0.758527 1.729689 0.758527 0.758527
2000-01-04 -1.340896 1.846883 -1.340896 1.682706
2000-01-05 -1.717693 0.888782 0.228440 0.901805
2000-01-06 1.171216 0.520260 1.171216 1.171216
2000-01-07 -0.303421 -0.303421 0.306996 -0.303421
2000-01-08 0.384316 1.574159 1.588931 0.476720
Введено в версии 0.18.1.
Где можно принять вызываемую функцию в качестве условия и аргументы other. Функция должна принимать один аргумент (вызываемую Series или DataFrame) и возвращать действительный результат как условие и аргумент other.
In [174]: df3 = pd.DataFrame({'A': [1, 2, 3],
.....: 'B': [4, 5, 6],
.....: 'C': [7, 8, 9]})
.....:
In [175]: df3.where(lambda x: x > 4, lambda x: x + 10)
Out[175]:
A B C
0 11 14 7
1 12 5 8
2 13 6 9
маска
mask является обратной булевой операцией where.
In [176]: s.mask(s >= 0)
Out[176]:
4 NaN
3 NaN
2 NaN
1 NaN
0 NaN
dtype: float64
In [177]: df.mask(df >= 0)
Out[177]:
A B C D
2000-01-01 -1.282782 NaN -1.071357 NaN
2000-01-02 NaN NaN NaN -0.744471
2000-01-03 NaN NaN -0.964980 -0.845696
2000-01-04 -1.340896 NaN -1.328865 NaN
2000-01-05 -1.717693 NaN NaN NaN
2000-01-06 NaN NaN -1.197071 -1.066969
2000-01-07 -0.303421 -0.858447 NaN -0.028665
2000-01-08 NaN NaN NaN NaN
Метод query() (Экспериментальный)
Введено в версии 0.13.
DataFrame объекты имеют метод query(), который позволяет выполнять выбор с помощью выражения.
Можно получить значение фрейма, где столбец b имеет значения между значениями столбцов a и c. Например:
In [178]: n = 10
In [179]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [180]: df
Out[180]:
a b c
0 0.287377 0.914479 0.010693
1 0.474521 0.259146 0.607235
2 0.317092 0.266463 0.712394
3 0.408662 0.705810 0.920000
4 0.740984 0.109157 0.456672
5 0.412527 0.455994 0.236508
6 0.769694 0.549898 0.530660
7 0.093826 0.022924 0.425014
8 0.029375 0.964501 0.680094
9 0.260754 0.755564 0.761476
# pure python
In [181]: df[(df.a < df.b) & (df.b < df.c)]
Out[181]:
a b c
3 0.408662 0.705810 0.920000
9 0.260754 0.755564 0.761476
# query
In [182]: df.query('(a < b) & (b < c)')
Out[182]:
a b c
3 0.408662 0.705810 0.920000
9 0.260754 0.755564 0.761476
Сделать то же самое, но использовать именованный индекс, если столбца с именем a нет.
In [183]: df = pd.DataFrame(np.random.randint(n / 2, size=(n, 2)), columns=list('bc'))
In [184]: df.index.name = 'a'
In [185]: df
Out[185]:
b c
a
0 3 4
1 1 1
2 0 4
3 4 3
4 1 3
5 3 3
6 2 3
7 4 0
8 3 4
9 2 1
In [186]: df.query('a < b and b < c')
Out[186]:
b c
a
0 3 4
Если вы не хотите или не можете назвать свой индекс, вы можете использовать имя index в выражении запроса:
In [187]: df = pd.DataFrame(np.random.randint(n, size=(n, 2)), columns=list('bc'))
In [188]: df
Out[188]:
b c
0 8 8
1 8 1
2 3 4
3 8 4
4 8 9
5 3 5
6 9 4
7 0 8
8 9 2
9 3 9
In [189]: df.query('index < b < c')
Out[189]:
b c
2 3 4
4 8 9
Примечание
Если имя вашего индекса совпадает с именем столбца, имя столбца имеет приоритет. Например,
In [190]: df = pd.DataFrame({'a': np.random.randint(5, size=5)})
In [191]: df.index.name = 'a'
In [192]: df.query('a > 2') # uses the column 'a', not the index
Out[192]:
a
a
1 3
2 3
4 3
Вы по-прежнему можете использовать индекс в выражении запроса, используя специальный идентификатор «index»:
In [193]: df.query('index > 2')
Out[193]:
a
a
3 1
4 3
Если по какой-то причине у вас есть столбец с именем index, вы можете сослаться на индекс как на ilevel_0, но в этом случае следует рассмотреть возможность переименования столбцов на что-то менее неоднозначное.
MultiIndex query() Синтаксис
Вы также можете использовать уровни DataFrame с MultiIndex так, как будто они являются столбцами в фрейме:
In [194]: n = 10
In [195]: colors = np.random.choice(['red', 'green'], size=n)
In [196]: foods = np.random.choice(['eggs', 'ham'], size=n)
In [197]: colors
Out[197]:
array(['red', 'red', 'green', 'green', 'red', 'green', 'red', 'green',
'red', 'red'],
dtype='|S5')
In [198]: foods
Out[198]:
array(['ham', 'eggs', 'ham', 'eggs', 'ham', 'eggs', 'ham', 'eggs', 'ham',
'ham'],
dtype='|S4')
In [199]: index = pd.MultiIndex.from_arrays([colors, foods], names=['color', 'food'])
In [200]: df = pd.DataFrame(np.random.randn(n, 2), index=index)
In [201]: df
Out[201]:
0 1
color food
red ham 0.694028 0.177154
eggs 0.535700 -0.506675
green ham 0.421335 -1.289076
eggs -0.178069 -0.271841
red ham 1.406993 -1.334905
green eggs -1.087664 -0.883833
red ham -1.554827 -0.118953
green eggs -1.460084 -0.020351
red ham -0.256125 0.358575
ham 1.112033 -0.200521
In [202]: df.query('color == "red"')
Out[202]:
0 1
color food
red ham 0.694028 0.177154
eggs 0.535700 -0.506675
ham 1.406993 -1.334905
ham -1.554827 -0.118953
ham -0.256125 0.358575
ham 1.112033 -0.200521
Если уровни MultiIndex не имеют имён, вы можете сослаться на них с помощью специальных имён:
In [203]: df.index.names = [None, None]
In [204]: df
Out[204]:
0 1
red ham 0.694028 0.177154
eggs 0.535700 -0.506675
green ham 0.421335 -1.289076
eggs -0.178069 -0.271841
red ham 1.406993 -1.334905
green eggs -1.087664 -0.883833
red ham -1.554827 -0.118953
green eggs -1.460084 -0.020351
red ham -0.256125 0.358575
ham 1.112033 -0.200521
In [205]: df.query('ilevel_0 == "red"')
Out[205]:
0 1
red ham 0.694028 0.177154
eggs 0.535700 -0.506675
ham 1.406993 -1.334905
ham -1.554827 -0.118953
ham -0.256125 0.358575
ham 1.112033 -0.200521
Конвенция ilevel_0, что означает «уровень индекса 0» для 0-го уровня index.
query() Сценарии использования
Сценарий использования для query() — это когда у вас есть набор объектов DataFrame, которые имеют общее подмножество имён столбцов (или уровней/имён индексов). Вы можете передать один и тот же запрос в оба фрейма без необходимости указывать, какой фрейм вас интересует.
In [206]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [207]: df
Out[207]:
a b c
0 0.941841 0.116093 0.886792
1 0.067661 0.252916 0.116448
2 0.733342 0.951527 0.946892
3 0.059026 0.548135 0.950113
4 0.643295 0.330300 0.192439
5 0.174329 0.697941 0.358970
6 0.972314 0.789179 0.293847
7 0.374439 0.739133 0.221186
8 0.900625 0.534438 0.608763
9 0.166933 0.731582 0.965147
In [208]: df2 = pd.DataFrame(np.random.rand(n + 2, 3), columns=df.columns)
In [209]: df2
Out[209]:
a b c
0 0.763981 0.372737 0.639792
1 0.702270 0.730804 0.134089
2 0.522758 0.311910 0.656542
3 0.258647 0.655096 0.654920
4 0.452594 0.454307 0.918260
5 0.581556 0.470410 0.417434
6 0.552021 0.483125 0.807046
7 0.277950 0.213500 0.471524
8 0.501458 0.141708 0.763617
9 0.081639 0.906284 0.480101
10 0.472250 0.380061 0.822149
11 0.459151 0.851196 0.125791
In [210]: expr = '0.0 <= a <= c <= 0.5'
In [211]: map(lambda frame: frame.query(expr), [df, df2])
Out[211]:
[ a b c
1 0.067661 0.252916 0.116448
5 0.174329 0.697941 0.358970, a b c
7 0.277950 0.213500 0.471524
9 0.081639 0.906284 0.480101]
query() Сравнение синтаксиса Python и pandas
Полный синтаксис numpy
In [212]: df = pd.DataFrame(np.random.randint(n, size=(n, 3)), columns=list('abc'))
In [213]: df
Out[213]:
a b c
0 8 6 2
1 9 8 5
2 7 7 9
3 7 6 9
4 6 9 7
5 1 0 7
6 8 9 1
7 0 7 2
8 7 2 6
9 2 2 2
In [214]: df.query('(a < b) & (b < c)')
Out[214]:
Empty DataFrame
Columns: [a, b, c]
Index: []
In [215]: df[(df.a < df.b) & (df.b < df.c)]
Out[215]:
Empty DataFrame
Columns: [a, b, c]
Index: []
Немного лучше за счёт удаления скобок (потому что операторы сравнения связываются сильнее, чем &/|)
In [216]: df.query('a < b & b < c')
Out[216]:
Empty DataFrame
Columns: [a, b, c]
Index: []
Использование английских слов вместо символов
In [217]: df.query('a < b and b < c')
Out[217]:
Empty DataFrame
Columns: [a, b, c]
Index: []
Очень близко к тому, как вы можете это написать на бумаге
In [218]: df.query('a < b < c')
Out[218]:
Empty DataFrame
Columns: [a, b, c]
Index: []
Операторы in и not in
query() также поддерживает специальное использование операторов сравнения Python in и not in, предоставляя лаконичный синтаксис для вызова метода isin объекта Series или DataFrame.
# get all rows where columns "a" and "b" have overlapping values
In [219]: df = pd.DataFrame({'a': list('aabbccddeeff'), 'b': list('aaaabbbbcccc'),
.....: 'c': np.random.randint(5, size=12),
.....: 'd': np.random.randint(9, size=12)})
.....:
In [220]: df
Out[220]:
a b c d
0 a a 4 8
1 a a 4 0
2 b a 3 1
3 b a 3 5
4 c b 3 0
5 c b 0 4
6 d b 2 6
7 d b 1 2
8 e c 4 3
9 e c 2 0
10 f c 1 6
11 f c 1 5
In [221]: df.query('a in b')
Out[221]:
a b c d
0 a a 4 8
1 a a 4 0
2 b a 3 1
3 b a 3 5
4 c b 3 0
5 c b 0 4
# How you'd do it in pure Python
In [222]: df[df.a.isin(df.b)]
Out[222]:
a b c d
0 a a 4 8
1 a a 4 0
2 b a 3 1
3 b a 3 5
4 c b 3 0
5 c b 0 4
In [223]: df.query('a not in b')
Out[223]:
a b c d
6 d b 2 6
7 d b 1 2
8 e c 4 3
9 e c 2 0
10 f c 1 6
11 f c 1 5
# pure Python
In [224]: df[~df.a.isin(df.b)]
Out[224]:
a b c d
6 d b 2 6
7 d b 1 2
8 e c 4 3
9 e c 2 0
10 f c 1 6
11 f c 1 5
Можно объединить это с другими выражениями для очень лаконичных запросов:
# rows where cols a and b have overlapping values and col c's values are less than col d's
In [225]: df.query('a in b and c < d')
Out[225]:
a b c d
0 a a 4 8
3 b a 3 5
5 c b 0 4
# pure Python
In [226]: df[df.b.isin(df.a) & (df.c < df.d)]
Out[226]:
a b c d
0 a a 4 8
3 b a 3 5
5 c b 0 4
6 d b 2 6
7 d b 1 2
10 f c 1 6
11 f c 1 5
Примечание
Обратите внимание, что in и not in оцениваются в Python, так как у numexpr нет эквивалента этой операции. Однако, только выражение in/not in само по себе оценивается в чистом Python. Например, в выражении
df.query('a in b + c + d')
(b + c + d) оценивается numexpr, а затем выполняется операция in в обычном Python. В общем, любые операции, которые можно оценить с помощью numexpr, будут.
Специальное использование оператора == с объектами list
Сравнение list значений со столбцом с помощью ==/!= работает аналогично in/not in
In [227]: df.query('b == ["a", "b", "c"]')
Out[227]:
a b c d
0 a a 4 8
1 a a 4 0
2 b a 3 1
3 b a 3 5
4 c b 3 0
5 c b 0 4
6 d b 2 6
7 d b 1 2
8 e c 4 3
9 e c 2 0
10 f c 1 6
11 f c 1 5
# pure Python
In [228]: df[df.b.isin(["a", "b", "c"])]
Out[228]:
a b c d
0 a a 4 8
1 a a 4 0
2 b a 3 1
3 b a 3 5
4 c b 3 0
5 c b 0 4
6 d b 2 6
7 d b 1 2
8 e c 4 3
9 e c 2 0
10 f c 1 6
11 f c 1 5
In [229]: df.query('c == [1, 2]')
Out[229]:
a b c d
6 d b 2 6
7 d b 1 2
9 e c 2 0
10 f c 1 6
11 f c 1 5
In [230]: df.query('c != [1, 2]')
Out[230]:
a b c d
0 a a 4 8
1 a a 4 0
2 b a 3 1
3 b a 3 5
4 c b 3 0
5 c b 0 4
8 e c 4 3
# using in/not in
In [231]: df.query('[1, 2] in c')
Out[231]:
a b c d
6 d b 2 6
7 d b 1 2
9 e c 2 0
10 f c 1 6
11 f c 1 5
In [232]: df.query('[1, 2] not in c')
Out[232]:
a b c d
0 a a 4 8
1 a a 4 0
2 b a 3 1
3 b a 3 5
4 c b 3 0
5 c b 0 4
8 e c 4 3
# pure Python
In [233]: df[df.c.isin([1, 2])]
Out[233]:
a b c d
6 d b 2 6
7 d b 1 2
9 e c 2 0
10 f c 1 6
11 f c 1 5
Булевы операторы
Вы можете инвертировать булевы выражения с помощью слова not или оператора ~.
In [234]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [235]: df['bools'] = np.random.rand(len(df)) > 0.5
In [236]: df.query('~bools')
Out[236]:
a b c bools
3 0.400348 0.495503 0.815711 False
4 0.126361 0.328858 0.356186 False
5 0.620423 0.750739 0.718272 False
8 0.900520 0.883878 0.452325 False
In [237]: df.query('not bools')
Out[237]:
a b c bools
3 0.400348 0.495503 0.815711 False
4 0.126361 0.328858 0.356186 False
5 0.620423 0.750739 0.718272 False
8 0.900520 0.883878 0.452325 False
In [238]: df.query('not bools') == df[~df.bools]
Out[238]:
a b c bools
3 True True True True
4 True True True True
5 True True True True
8 True True True True
Конечно, выражения также могут быть произвольно сложными.
# short query syntax
In [239]: shorter = df.query('a < b < c and (not bools) or bools > 2')
# equivalent in pure Python
In [240]: longer = df[(df.a < df.b) & (df.b < df.c) & (~df.bools) | (df.bools > 2)]
In [241]: shorter
Out[241]:
a b c bools
3 0.400348 0.495503 0.815711 False
4 0.126361 0.328858 0.356186 False
In [242]: longer
Out[242]:
a b c bools
3 0.400348 0.495503 0.815711 False
4 0.126361 0.328858 0.356186 False
In [243]: shorter == longer
Out[243]:
a b c bools
3 True True True True
4 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 [244]: 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 [245]: df2
Out[245]:
a b c
0 one x 0.340520
1 one y -0.252382
2 two x -1.206215
3 two y 1.233196
4 two x 1.671173
5 three x 0.120314
6 four x 0.779461
In [246]: df2.duplicated('a')
Out[246]:
0 False
1 True
2 False
3 True
4 True
5 False
6 False
dtype: bool
In [247]: df2.duplicated('a', keep='last')
Out[247]:
0 True
1 False
2 True
3 True
4 False
5 False
6 False
dtype: bool
In [248]: df2.duplicated('a', keep=False)
Out[248]:
0 True
1 True
2 True
3 True
4 True
5 False
6 False
dtype: bool
In [249]: df2.drop_duplicates('a')
Out[249]:
a b c
0 one x 0.340520
2 two x -1.206215
5 three x 0.120314
6 four x 0.779461
In [250]: df2.drop_duplicates('a', keep='last')
Out[250]:
a b c
1 one y -0.252382
4 two x 1.671173
5 three x 0.120314
6 four x 0.779461
In [251]: df2.drop_duplicates('a', keep=False)
Out[251]:
a b c
5 three x 0.120314
6 four x 0.779461
Также вы можете передать список столбцов для определения дубликатов.
In [252]: df2.duplicated(['a', 'b'])
Out[252]:
0 False
1 False
2 False
3 False
4 True
5 False
6 False
dtype: bool
In [253]: df2.drop_duplicates(['a', 'b'])
Out[253]:
a b c
0 one x 0.340520
1 one y -0.252382
2 two x -1.206215
3 two y 1.233196
5 three x 0.120314
6 four x 0.779461
Для удаления дубликатов по значению индекса используйте Index.duplicated, а затем выполните срез. Те же опции доступны в параметре keep.
In [254]: df3 = pd.DataFrame({'a': np.arange(6),
.....: 'b': np.random.randn(6)},
.....: index=['a', 'a', 'b', 'c', 'b', 'a'])
.....:
In [255]: df3
Out[255]:
a b
a 0 -0.867696
a 1 -0.422603
b 2 -1.482245
c 3 -0.878999
b 4 0.511972
a 5 1.320469
In [256]: df3.index.duplicated()
Out[256]: array([False, True, False, False, True, True], dtype=bool)
In [257]: df3[~df3.index.duplicated()]
Out[257]:
a b
a 0 -0.867696
b 2 -1.482245
c 3 -0.878999
In [258]: df3[~df3.index.duplicated(keep='last')]
Out[258]:
a b
c 3 -0.878999
b 4 0.511972
a 5 1.320469
In [259]: df3[~df3.index.duplicated(keep=False)]
Out[259]:
a b
c 3 -0.878999
Свойство get() типа словарь
В каждом из Series, DataFrame и Panel есть метод get , который может вернуть значение по умолчанию.
In [260]: s = pd.Series([1,2,3], index=['a','b','c'])
In [261]: s.get('a') # equivalent to s['a']
Out[261]: 1
In [262]: s.get('x', default=-1)
Out[262]: -1
Метод select()
Другой способ извлечения срезов из объекта — метод select Series, DataFrame и Panel. Этот метод следует использовать только тогда, когда нет более прямого способа. select принимает функцию, которая работает со метками по axis и возвращает булево значение. Например:
In [263]: df.select(lambda x: x == 'A', axis=1)
Out[263]:
A
2000-01-01 0.888261
2000-01-02 1.203830
2000-01-03 0.202067
2000-01-04 0.051667
2000-01-05 -0.728628
2000-01-06 0.257538
2000-01-07 0.362494
2000-01-08 -1.335476
Метод lookup()
Иногда требуется извлечь набор значений, заданных последовательностью меток строк и столбцов, и метод lookup позволяет это сделать, возвращая массив numpy. Например,
In [264]: dflookup = pd.DataFrame(np.random.rand(20,4), columns = ['A','B','C','D']) In [265]: dflookup.lookup(list(range(0,10,2)), ['B','C','A','B','D']) Out[265]: array([ 0.1473, 0.7212, 0.521 , 0.6512, 0.2522])
Объекты индексов
Класс pandas Index и его подклассы можно рассматривать как реализацию упорядоченного мультимножества. Повторы разрешены. Однако, если вы попытаетесь преобразовать объект Index с дублирующимися записями в set, будет поднято исключение.
Index также предоставляет инфраструктуру, необходимую для поиска, выравнивания данных и повторного индексирования. Самый простой способ создать Index напрямую — передать list или другую последовательность в Index:
In [266]: index = pd.Index(['e', 'd', 'a', 'b']) In [267]: index Out[267]: Index([u'e', u'd', u'a', u'b'], dtype='object') In [268]: 'd' in index Out[268]: True
Вы также можете передать name для хранения в индексе:
In [269]: index = pd.Index(['e', 'd', 'a', 'b'], name='something') In [270]: index.name Out[270]: 'something'
Имя, если оно задано, будет отображаться в консоли:
In [271]: index = pd.Index(list(range(5)), name='rows') In [272]: columns = pd.Index(['A', 'B', 'C'], name='cols') In [273]: df = pd.DataFrame(np.random.randn(5, 3), index=index, columns=columns) In [274]: df Out[274]: cols A B C rows 0 1.110384 0.381978 -1.112425 1 0.037163 -0.139385 0.480202 2 1.302333 -0.010130 1.223824 3 0.185778 0.436259 0.678101 4 0.311369 -0.528378 -0.674808 In [275]: df['A'] Out[275]: rows 0 1.110384 1 0.037163 2 1.302333 3 0.185778 4 0.311369 Name: A, dtype: float64
Установка метаданных
Введено в версии 0.13.0.
Индексы «в основном неизменяемы», но можно устанавливать и изменять их метаданные, такие как индекс name (или, для MultiIndex, levels и labels).
Вы можете использовать rename, set_names, set_levels, и set_labels для непосредственного задания этих атрибутов. По умолчанию они возвращают копию; однако, вы можете указать inplace=True для изменения данных на месте.
См. Расширенный индексирование для использования MultiIndexes.
In [276]: ind = pd.Index([1, 2, 3])
In [277]: ind.rename("apple")
Out[277]: Int64Index([1, 2, 3], dtype='int64', name=u'apple')
In [278]: ind
Out[278]: Int64Index([1, 2, 3], dtype='int64')
In [279]: ind.set_names(["apple"], inplace=True)
In [280]: ind.name = "bob"
In [281]: ind
Out[281]: Int64Index([1, 2, 3], dtype='int64', name=u'bob')
Введено в версии 0.15.0.
set_names, set_levels, и set_labels также принимают необязательный аргумент level`
In [282]: index = pd.MultiIndex.from_product([range(3), ['one', 'two']], names=['first', 'second'])
In [283]: index
Out[283]:
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 [284]: index.levels[1]
Out[284]: Index([u'one', u'two'], dtype='object', name=u'second')
In [285]: index.set_levels(["a", "b"], level=1)
Out[285]:
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'])
Операции над множествами для объектов индекса
Предупреждение
В версии 0.15.0 операции с множествами + и - были устаревшими, чтобы предоставить их для числовых операций с определёнными типами индексов. + может быть заменено на .union() или |, а - на .difference().
Две основные операции — union (|), intersection (&). Их можно непосредственно вызвать как методы экземпляра или использовать через перегруженные операторы. Разность предоставляется через метод .difference().
In [286]: a = pd.Index(['c', 'b', 'a']) In [287]: b = pd.Index(['c', 'e', 'd']) In [288]: a | b Out[288]: Index([u'a', u'b', u'c', u'd', u'e'], dtype='object') In [289]: a & b Out[289]: Index([u'c'], dtype='object') In [290]: a.difference(b) Out[290]: Index([u'a', u'b'], dtype='object')
Также доступна операция symmetric_difference (^), которая возвращает элементы, присутствующие в idx1 или idx2, но не в обоих одновременно. Это эквивалентно индексу, созданному с помощью idx1.difference(idx2).union(idx2.difference(idx1)), с удалением дубликатов.
In [291]: idx1 = pd.Index([1, 2, 3, 4]) In [292]: idx2 = pd.Index([2, 3, 4, 5]) In [293]: idx1.symmetric_difference(idx2) Out[293]: Int64Index([1, 5], dtype='int64') In [294]: idx1 ^ idx2 Out[294]: Int64Index([1, 5], dtype='int64')
Пропущенные значения
Введено в версии 0.17.1.
Важно
Несмотря на то, что Index может содержать пропущенные значения (NaN), их следует избегать, если вы не хотите получить непредвиденные результаты. Например, некоторые операции неявно исключают пропущенные значения.
Index.fillna заполняет пропущенные значения указанным скалярным значением.
In [295]: idx1 = pd.Index([1, np.nan, 3, 4])
In [296]: idx1
Out[296]: Float64Index([1.0, nan, 3.0, 4.0], dtype='float64')
In [297]: idx1.fillna(2)
Out[297]: Float64Index([1.0, 2.0, 3.0, 4.0], dtype='float64')
In [298]: idx2 = pd.DatetimeIndex([pd.Timestamp('2011-01-01'), pd.NaT, pd.Timestamp('2011-01-03')])
In [299]: idx2
Out[299]: DatetimeIndex(['2011-01-01', 'NaT', '2011-01-03'], dtype='datetime64[ns]', freq=None)
In [300]: idx2.fillna(pd.Timestamp('2011-01-02'))
Out[300]: DatetimeIndex(['2011-01-01', '2011-01-02', '2011-01-03'], dtype='datetime64[ns]', freq=None)
Установка/Сброс индекса
Иногда вам потребуется загрузить или создать набор данных в DataFrame и добавить индекс после того, как вы это уже сделали. Есть несколько способов.
Установка индекса
DataFrame имеет метод set_index, который принимает имя столбца (для обычного Index) или список имён столбцов (для MultiIndex), чтобы создать новый индексированный DataFrame:
In [301]: data
Out[301]:
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 [302]: indexed1 = data.set_index('c')
In [303]: indexed1
Out[303]:
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 [304]: indexed2 = data.set_index(['a', 'b'])
In [305]: indexed2
Out[305]:
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 [306]: frame = data.set_index('c', drop=False)
In [307]: frame = frame.set_index(['a', 'b'], append=True)
In [308]: frame
Out[308]:
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 [309]: data.set_index('c', drop=False)
Out[309]:
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 [310]: data.set_index(['a', 'b'], inplace=True)
In [311]: data
Out[311]:
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 [312]: data
Out[312]:
c d
a b
bar one z 1.0
two y 2.0
foo one x 3.0
two w 4.0
In [313]: data.reset_index()
Out[313]:
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 [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
In [315]: frame.reset_index(level=1)
Out[315]:
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, который, если он истинный, просто отбрасывает индекс вместо размещения значений индекса в столбцах DataFrame.
Примечание
Метод reset_index раньше назывался delevel, но теперь он устарел.
Добавление временного индекса
Если вы создаете индекс самостоятельно, вы можете просто присвоить его полю index:
data.index = index
Возвращение представления или копии
При установке значений в объекте pandas необходимо соблюдать осторожность, чтобы избежать так называемого chained indexing. Вот пример.
In [316]: dfmi = pd.DataFrame([list('abcd'),
.....: list('efgh'),
.....: list('ijkl'),
.....: list('mnop')],
.....: columns=pd.MultiIndex.from_product([['one','two'],
.....: ['first','second']]))
.....:
In [317]: dfmi
Out[317]:
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 [318]: dfmi['one']['second'] Out[318]: 0 b 1 f 2 j 3 n Name: second, dtype: object
In [319]: dfmi.loc[:,('one','second')]
Out[319]:
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 [320]: 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 [321]: 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 [322]: dfc = pd.DataFrame({'A':['aaa','bbb','ccc'],'B':[1,2,3]})
In [323]: dfc.loc[0,'A'] = 11
In [324]: dfc
Out[324]:
A B
0 11 1
1 bbb 2
2 ccc 3
Это может работать иногда, но не гарантируется, поэтому его следует избегать
In [325]: dfc = dfc.copy()
In [326]: dfc['A'][0] = 111
In [327]: dfc
Out[327]:
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.18.1/indexing.html