Spec-Zone.ru › pandas 0.18

Индексирование и выбор данных

Информация о маркировке осей в объектах 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 для больших фреймов

_images/query-perf.png

Примечание

Преимущества использования движка numexpr с DataFrame.query() станут заметны только при количестве строк в кадре более приблизительно 200 000.

_images/query-perf-small.png

Этот график был создан с помощью 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API