Spec-Zone.ru › pandas 0.19

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

Информация о метках осей в объектах pandas служит для многих целей:

  • Идентифицирует данные (т. е. предоставляет метаданные) с использованием известных индикаторов, важных для анализа, визуализации и интерактивного отображения в консоли
  • Обеспечивает автоматическое и явное выравнивание данных
  • Позволяет интуитивно получать и устанавливать подмножества набора данных

В этом разделе мы сосредоточимся на последнем пункте: как нарезать, измельчить и в целом получать и устанавливать подмножества объектов pandas. Основное внимание будет уделено Series и DataFrame, так как они получили больше внимания в этой области. В будущем ожидается больше работы с многомерными структурами данных (включая Panel), особенно в области расширенного индексирования с использованием меток.

Примечание

Операторы индексирования Python и NumPy [] и оператор атрибутов . обеспечивают быстрый и легкий доступ к структурам данных pandas в широком диапазоне случаев использования. Это делает интерактивную работу интуитивно понятной, поскольку практически ничего нового не нужно изучать, если вы уже знаете, как работать со словарями Python и массивами NumPy. Однако, поскольку тип данных, к которому нужно получить доступ, заранее неизвестен, непосредственное использование стандартных операторов имеет некоторые ограничения по оптимизации. Для кода в производстве мы рекомендуем воспользоваться оптимизированными методами доступа к данным pandas, представленными в этой главе.

Предупреждение

Возвращается ли копия или ссылка при операции установки, может зависеть от контекста. Это иногда называется chained assignment и следует избегать. См. Возвращение представления вместо копии

Предупреждение

В версии 0.15.0 Index внутренне был переработан, чтобы больше не наследоваться от ndarray, а вместо этого наследоваться от PandasObject, подобно остальным объектам pandas. Это должно быть прозрачное изменение с очень ограниченными последствиями для API (см. Внутренняя рефакторинг)

Предупреждение

Индексирование на основе целого числа с плавающей точкой было уточнено в версии 0.18.0. Подробнее о изменениях см. здесь.

См. Многоуровневый индекс / Расширенное индексирование для MultiIndex и более подробной документации по расширенному индексированию.

См. справочник для некоторых расширенных стратегий

Различные варианты индексирования

Впервые добавлено в версии 0.11.0.

Выбор объекта был дополнен рядом запросов пользователей, чтобы обеспечить более явное индексирование на основе местоположения. Pandas теперь поддерживает три типа многоосевого индексирования.

  • .loc в основном основан на метках, но также может использоваться с булевым массивом. .loc вызовет KeyError, если элементы не найдены. Допустимые входные данные:

    • Единая метка, например, 5 или 'a' (обратите внимание, что 5 интерпретируется как метка индекса. Это использование не является целочисленной позицией вдоль индекса)
    • Список или массив меток ['a', 'b', 'c']
    • Объект среза с метками 'a':'f' (обратите внимание, что в отличие от обычных срезов Python, оба начала и конца включены!)
    • Булев массив
    • Функция callable с одним аргументом (вызываемый Series, DataFrame или Panel), которая возвращает допустимый результат для индексирования (один из вышеперечисленных)

      Впервые добавлено в версии 0.18.1.

    См. подробнее в разделе Выбор по метке

  • .iloc в основном основан на целочисленной позиции (от 0 до length-1 оси), но также может использоваться с булевым массивом. .iloc вызовет IndexError, если запрашиваемый индексатор находится вне границ, за исключением индексаторов среза, которые позволяют индексировать значения за пределами границ. (это соответствует семантике Python/NumPy среза). Допустимые входные данные:

    • Целое число, например, 5
    • Список или массив целых чисел [4, 3, 0]
    • Объект среза с целыми числами 1:7
    • Булев массив
    • Функция callable с одним аргументом (вызываемый Series, DataFrame или Panel), которая возвращает допустимый результат для индексирования (один из вышеперечисленных)

      Впервые добавлено в версии 0.18.1.

    См. подробнее в разделе Выбор по позиции

  • .ix поддерживает смешанный доступ на основе целых чисел и меток. Он в основном основан на метках, но переходит к целочисленному позиционному доступу, если соответствующая ось имеет целочисленный тип. .ix является наиболее общим и поддерживает любые входные данные в .loc и .iloc. .ix также поддерживает схемы меток с плавающей точкой. .ix особенно полезен при работе с иерархическими индексами, основанными на смешанных позиционных и метках.

    Однако, когда ось основана на целых числах, только доступ на основе меток, а не позиционный доступ, поддерживается. Таким образом, в таких случаях обычно лучше быть явным и использовать .iloc или .loc.

    См. подробнее в разделе Расширенное индексирование и Расширенные иерархические.

  • .loc, .iloc, .ix и также [] индексирование могут принимать callable в качестве индексатора. Подробнее см. в разделе Выбор по вызываемому методу.

Получение значений из объекта с выбором по нескольким осям использует следующий формат (используя .loc в качестве примера, но применяется также к .iloc и .ix). Любой из осевых аксессоров может быть пустым срезом :. Оси, не указанные в спецификации, предполагаются :. (например, p.loc['a'] эквивалентно p.loc['a', :, :])

Тип объекта Индексаторы
Series s.loc[indexer]
DataFrame df.loc[row_indexer,column_indexer]
Panel p.loc[item_indexer,major_indexer,minor_indexer]

Основы

Как упоминалось при представлении структур данных в предыдущем разделе, основная функция индексирования с помощью [] (также известная как __getitem__ для тех, кто знаком с реализацией поведения класса в Python) заключается в выделении двумерных срезов. Таким образом,

Тип объекта Выбор Тип возвращаемого значения
Series series[label] скалярное значение
DataFrame frame[colname] Series соответствующий столбцу
Panel panel[itemname] DataFrame соответствующий имени элемента

Здесь мы создаем простой временной ряд данных, чтобы проиллюстрировать функциональность индексирования:

In [1]: dates = pd.date_range('1/1/2000', periods=8)

In [2]: df = pd.DataFrame(np.random.randn(8, 4), index=dates, columns=['A', 'B', 'C', 'D'])

In [3]: df
Out[3]: 
                   A         B         C         D
2000-01-01  0.469112 -0.282863 -1.509059 -1.135632
2000-01-02  1.212112 -0.173215  0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929  1.071804
2000-01-04  0.721555 -0.706771 -1.039575  0.271860
2000-01-05 -0.424972  0.567020  0.276232 -1.087401
2000-01-06 -0.673690  0.113648 -1.478427  0.524988
2000-01-07  0.404705  0.577046 -1.715002 -1.039268
2000-01-08 -0.370647 -1.157892 -1.344312  0.844885

In [4]: panel = pd.Panel({'one' : df, 'two' : df - df.mean()})

In [5]: panel
Out[5]: 
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 8 (major_axis) x 4 (minor_axis)
Items axis: one to two
Major_axis axis: 2000-01-01 00:00:00 to 2000-01-08 00:00:00
Minor_axis axis: A to D

Примечание

Ни одна из функций индексирования не является специфичной для временных рядов, если не указано иное.

Таким образом, как указано выше, у нас есть самое базовое индексирование с использованием []:

In [6]: s = df['A']

In [7]: s[dates[5]]
Out[7]: -0.67368970808837059

In [8]: panel['two']
Out[8]: 
                   A         B         C         D
2000-01-01  0.409571  0.113086 -0.610826 -0.936507
2000-01-02  1.152571  0.222735  1.017442 -0.845111
2000-01-03 -0.921390 -1.708620  0.403304  1.270929
2000-01-04  0.662014 -0.310822 -0.141342  0.470985
2000-01-05 -0.484513  0.962970  1.174465 -0.888276
2000-01-06 -0.733231  0.509598 -0.580194  0.724113
2000-01-07  0.345164  0.972995 -0.816769 -0.840143
2000-01-08 -0.430188 -0.761943 -0.446079  1.044010

Вы можете передать список столбцов в [], чтобы выбрать столбцы в этом порядке. Если столбец не содержится в DataFrame, будет вызвано исключение. Несколько столбцов также могут быть установлены таким образом:

In [9]: df
Out[9]: 
                   A         B         C         D
2000-01-01  0.469112 -0.282863 -1.509059 -1.135632
2000-01-02  1.212112 -0.173215  0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929  1.071804
2000-01-04  0.721555 -0.706771 -1.039575  0.271860
2000-01-05 -0.424972  0.567020  0.276232 -1.087401
2000-01-06 -0.673690  0.113648 -1.478427  0.524988
2000-01-07  0.404705  0.577046 -1.715002 -1.039268
2000-01-08 -0.370647 -1.157892 -1.344312  0.844885

In [10]: df[['B', 'A']] = df[['A', 'B']]

In [11]: df
Out[11]: 
                   A         B         C         D
2000-01-01 -0.282863  0.469112 -1.509059 -1.135632
2000-01-02 -0.173215  1.212112  0.119209 -1.044236
2000-01-03 -2.104569 -0.861849 -0.494929  1.071804
2000-01-04 -0.706771  0.721555 -1.039575  0.271860
2000-01-05  0.567020 -0.424972  0.276232 -1.087401
2000-01-06  0.113648 -0.673690 -1.478427  0.524988
2000-01-07  0.577046  0.404705 -1.715002 -1.039268
2000-01-08 -1.157892 -0.370647 -1.344312  0.844885

Это может быть полезно для применения преобразования (на месте) к подмножеству столбцов.

Предупреждение

pandas выравнивает все ОСИ при установке Series и DataFrame из .loc, .iloc и .ix.

Это не изменит df, потому что выравнивание столбцов происходит перед присвоением значений.

In [12]: df[['A', 'B']]
Out[12]: 
                   A         B
2000-01-01 -0.282863  0.469112
2000-01-02 -0.173215  1.212112
2000-01-03 -2.104569 -0.861849
2000-01-04 -0.706771  0.721555
2000-01-05  0.567020 -0.424972
2000-01-06  0.113648 -0.673690
2000-01-07  0.577046  0.404705
2000-01-08 -1.157892 -0.370647

In [13]: df.loc[:,['B', 'A']] = df[['A', 'B']]

In [14]: df[['A', 'B']]
Out[14]: 
                   A         B
2000-01-01 -0.282863  0.469112
2000-01-02 -0.173215  1.212112
2000-01-03 -2.104569 -0.861849
2000-01-04 -0.706771  0.721555
2000-01-05  0.567020 -0.424972
2000-01-06  0.113648 -0.673690
2000-01-07  0.577046  0.404705
2000-01-08 -1.157892 -0.370647

Правильный способ — использовать исходные значения

In [15]: df.loc[:,['B', 'A']] = df[['A', 'B']].values

In [16]: df[['A', 'B']]
Out[16]: 
                   A         B
2000-01-01  0.469112 -0.282863
2000-01-02  1.212112 -0.173215
2000-01-03 -0.861849 -2.104569
2000-01-04  0.721555 -0.706771
2000-01-05 -0.424972  0.567020
2000-01-06 -0.673690  0.113648
2000-01-07  0.404705  0.577046
2000-01-08 -0.370647 -1.157892

Доступ к атрибутам

Вы можете получить доступ к индексу Series, столбцу DataFrame и элементу Panel напрямую как атрибут:

In [17]: sa = pd.Series([1,2,3],index=list('abc'))

In [18]: dfa = df.copy()
In [19]: sa.b
Out[19]: 2

In [20]: dfa.A
Out[20]: 
2000-01-01    0.469112
2000-01-02    1.212112
2000-01-03   -0.861849
2000-01-04    0.721555
2000-01-05   -0.424972
2000-01-06   -0.673690
2000-01-07    0.404705
2000-01-08   -0.370647
Freq: D, Name: A, dtype: float64

In [21]: panel.one
Out[21]: 
                   A         B         C         D
2000-01-01  0.469112 -0.282863 -1.509059 -1.135632
2000-01-02  1.212112 -0.173215  0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929  1.071804
2000-01-04  0.721555 -0.706771 -1.039575  0.271860
2000-01-05 -0.424972  0.567020  0.276232 -1.087401
2000-01-06 -0.673690  0.113648 -1.478427  0.524988
2000-01-07  0.404705  0.577046 -1.715002 -1.039268
2000-01-08 -0.370647 -1.157892 -1.344312  0.844885

Вы можете использовать доступ к атрибуту для изменения существующего элемента Series или столбца DataFrame, но будьте осторожны: если вы попытаетесь использовать доступ к атрибуту для создания нового столбца, это произойдет без сообщений об ошибках, создавая новый атрибут вместо нового столбца.

In [22]: sa.a = 5

In [23]: sa
Out[23]: 
a    5
b    2
c    3
dtype: int64

In [24]: dfa.A = list(range(len(dfa.index)))  # ok if A already exists

In [25]: dfa
Out[25]: 
            A         B         C         D
2000-01-01  0 -0.282863 -1.509059 -1.135632
2000-01-02  1 -0.173215  0.119209 -1.044236
2000-01-03  2 -2.104569 -0.494929  1.071804
2000-01-04  3 -0.706771 -1.039575  0.271860
2000-01-05  4  0.567020  0.276232 -1.087401
2000-01-06  5  0.113648 -1.478427  0.524988
2000-01-07  6  0.577046 -1.715002 -1.039268
2000-01-08  7 -1.157892 -1.344312  0.844885

In [26]: dfa['A'] = list(range(len(dfa.index)))  # use this form to create a new column

In [27]: dfa
Out[27]: 
            A         B         C         D
2000-01-01  0 -0.282863 -1.509059 -1.135632
2000-01-02  1 -0.173215  0.119209 -1.044236
2000-01-03  2 -2.104569 -0.494929  1.071804
2000-01-04  3 -0.706771 -1.039575  0.271860
2000-01-05  4  0.567020  0.276232 -1.087401
2000-01-06  5  0.113648 -1.478427  0.524988
2000-01-07  6  0.577046 -1.715002 -1.039268
2000-01-08  7 -1.157892 -1.344312  0.844885
END_OF_DOCUMENT_MARKER

Предупреждение

  • Вы можете использовать этот доступ только в том случае, если элемент индекса является допустимым идентификатором Python, например, s.1 не разрешен. См. здесь для объяснения допустимых идентификаторов.
  • Атрибут не будет доступен, если он конфликтует с существующим именем метода, например, s.min не разрешен.
  • Аналогично, атрибут не будет доступен, если он конфликтует с любым из следующих элементов списка: index, major_axis, minor_axis, items, labels.
  • В любом из этих случаев стандартная индексация по-прежнему будет работать, например, s['1'], s['min'] и s['index'] будут обращаться к соответствующему элементу или столбцу.
  • Series/Panel доступны начиная с версии 0.13.0.

Если вы используете среду IPython, вы также можете использовать автодополнение, чтобы увидеть доступные атрибуты.

Вы также можете назначить dict строке DataFrame:

In [28]: x = pd.DataFrame({'x': [1, 2, 3], 'y': [3, 4, 5]})

In [29]: x.iloc[1] = dict(x=9, y=99)

In [30]: x
Out[30]: 
   x   y
0  1   3
1  9  99
2  3   5

Диапазоны срезов

Наиболее надежный и согласованный способ среза по произвольным осям описан в разделе Выбор по позиции, который подробно описывает метод .iloc. На данный момент мы объясняем семантику среза с помощью оператора [].

В Series синтаксис работает точно так же, как и с ndarray, возвращая срез значений и соответствующих меток:

In [31]: s[:5]
Out[31]: 
2000-01-01    0.469112
2000-01-02    1.212112
2000-01-03   -0.861849
2000-01-04    0.721555
2000-01-05   -0.424972
Freq: D, Name: A, dtype: float64

In [32]: s[::2]
Out[32]: 
2000-01-01    0.469112
2000-01-03   -0.861849
2000-01-05   -0.424972
2000-01-07    0.404705
Freq: 2D, Name: A, dtype: float64

In [33]: s[::-1]
Out[33]: 
2000-01-08   -0.370647
2000-01-07    0.404705
2000-01-06   -0.673690
2000-01-05   -0.424972
2000-01-04    0.721555
2000-01-03   -0.861849
2000-01-02    1.212112
2000-01-01    0.469112
Freq: -1D, Name: A, dtype: float64

Обратите внимание, что установка также работает:

In [34]: s2 = s.copy()

In [35]: s2[:5] = 0

In [36]: s2
Out[36]: 
2000-01-01    0.000000
2000-01-02    0.000000
2000-01-03    0.000000
2000-01-04    0.000000
2000-01-05    0.000000
2000-01-06   -0.673690
2000-01-07    0.404705
2000-01-08   -0.370647
Freq: D, Name: A, dtype: float64

В DataFrame срез внутри [] вырезает строки. Это в значительной степени обеспечивается удобством, так как это такая распространенная операция.

In [37]: df[:3]
Out[37]: 
                   A         B         C         D
2000-01-01  0.469112 -0.282863 -1.509059 -1.135632
2000-01-02  1.212112 -0.173215  0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929  1.071804

In [38]: df[::-1]
Out[38]: 
                   A         B         C         D
2000-01-08 -0.370647 -1.157892 -1.344312  0.844885
2000-01-07  0.404705  0.577046 -1.715002 -1.039268
2000-01-06 -0.673690  0.113648 -1.478427  0.524988
2000-01-05 -0.424972  0.567020  0.276232 -1.087401
2000-01-04  0.721555 -0.706771 -1.039575  0.271860
2000-01-03 -0.861849 -2.104569 -0.494929  1.071804
2000-01-02  1.212112 -0.173215  0.119209 -1.044236
2000-01-01  0.469112 -0.282863 -1.509059 -1.135632

Выбор по метке

Предупреждение

Возвращаемая при операции установки копия или ссылка могут зависеть от контекста. Это иногда называется chained assignment и следует избегать. См. Возвращение представления или копии

Предупреждение

.loc строго относится к слайсерам, которые не совместимы (или не могут быть преобразованы) с типом индекса. Например, использование целых чисел в DatetimeIndex. Это приведет к исключению TypeError.

In [39]: dfl = pd.DataFrame(np.random.randn(5,4), columns=list('ABCD'), index=pd.date_range('20130101',periods=5))

In [40]: dfl
Out[40]: 
                   A         B         C         D
2013-01-01  1.075770 -0.109050  1.643563 -1.469388
2013-01-02  0.357021 -0.674600 -1.776904 -0.968914
2013-01-03 -1.294524  0.413738  0.276662 -0.472035
2013-01-04 -0.013960 -0.362543 -0.006154 -0.923061
2013-01-05  0.895717  0.805244 -1.206412  2.565646
In [4]: dfl.loc[2:3]
TypeError: cannot do slice indexing on <class 'pandas.tseries.index.DatetimeIndex'> with these indexers [2] of <type 'int'>

Строковые значения в срезах могут быть преобразуемы в тип индекса и привести к естественному срезу.

In [41]: dfl.loc['20130102':'20130104']
Out[41]: 
                   A         B         C         D
2013-01-02  0.357021 -0.674600 -1.776904 -0.968914
2013-01-03 -1.294524  0.413738  0.276662 -0.472035
2013-01-04 -0.013960 -0.362543 -0.006154 -0.923061

pandas предоставляет набор методов для чисто метки индексации. Это протокол строгого включения. Хотя бы 1 из меток, которые вы запрашиваете, должна быть в индексе, в противном случае будет вызвано исключение KeyError! При срезе начальная граница включена, и конечная граница включена. Целые числа являются допустимыми метками, но они относятся к метке, а не к позиции.

Атрибут .loc является основным методом доступа. Допустимыми входными данными являются:

  • Одна метка, например, 5 или 'a' (обратите внимание, что 5 интерпретируется как метка индекса. Это использование не является позицией целого числа по индексу)
  • Список или массив меток ['a', 'b', 'c']
  • Объект среза с метками 'a':'f' (обратите внимание, что в отличие от обычных срезов Python, обе начальная и конечная границы включены!)
  • Массив булевых значений
  • callable, см. Выбор по вызываемому объекту
In [42]: s1 = pd.Series(np.random.randn(6),index=list('abcdef'))

In [43]: s1
Out[43]: 
a    1.431256
b    1.340309
c   -1.170299
d   -0.226169
e    0.410835
f    0.813850
dtype: float64

In [44]: s1.loc['c':]
Out[44]: 
c   -1.170299
d   -0.226169
e    0.410835
f    0.813850
dtype: float64

In [45]: s1.loc['b']
Out[45]: 1.3403088497993827

Обратите внимание, что установка также работает:

In [46]: s1.loc['c':] = 0

In [47]: s1
Out[47]: 
a    1.431256
b    1.340309
c    0.000000
d    0.000000
e    0.000000
f    0.000000
dtype: float64

С DataFrame

In [48]: df1 = pd.DataFrame(np.random.randn(6,4),
   ....:                    index=list('abcdef'),
   ....:                    columns=list('ABCD'))
   ....: 

In [49]: df1
Out[49]: 
          A         B         C         D
a  0.132003 -0.827317 -0.076467 -1.187678
b  1.130127 -1.436737 -1.413681  1.607920
c  1.024180  0.569605  0.875906 -2.211372
d  0.974466 -2.006747 -0.410001 -0.078638
e  0.545952 -1.219217 -1.226825  0.769804
f -1.281247 -0.727707 -0.121306 -0.097883

In [50]: df1.loc[['a', 'b', 'd'], :]
Out[50]: 
          A         B         C         D
a  0.132003 -0.827317 -0.076467 -1.187678
b  1.130127 -1.436737 -1.413681  1.607920
d  0.974466 -2.006747 -0.410001 -0.078638

Доступ через срезы меток

In [51]: df1.loc['d':, 'A':'C']
Out[51]: 
          A         B         C
d  0.974466 -2.006747 -0.410001
e  0.545952 -1.219217 -1.226825
f -1.281247 -0.727707 -0.121306

Для получения поперечного сечения с помощью метки (эквивалентно df.xs('a'))

In [52]: df1.loc['a']
Out[52]: 
A    0.132003
B   -0.827317
C   -0.076467
D   -1.187678
Name: a, dtype: float64

Для получения значений с помощью массива булевых значений

In [53]: df1.loc['a'] > 0
Out[53]: 
A     True
B    False
C    False
D    False
Name: a, dtype: bool

In [54]: df1.loc[:, df1.loc['a'] > 0]
Out[54]: 
          A
a  0.132003
b  1.130127
c  1.024180
d  0.974466
e  0.545952
f -1.281247

Для получения значения явно (эквивалентно устаревшему df.get_value('a','A'))

# this is also equivalent to ``df1.at['a','A']``
In [55]: df1.loc['a', 'A']
Out[55]: 0.13200317033032932

Выбор по позиции

Предупреждение

Возвращаемая при операции установки копия или ссылка могут зависеть от контекста. Это иногда называется chained assignment и следует избегать. См. Возвращение представления или копии

pandas предоставляет набор методов для получения чисто целочисленной индексации. Семантика тесно следует синтаксису срезов Python и numpy. Это целочисленная индексация. При срезе начальная граница включена, а верхняя граница исключена. Попытка использовать не целое число, даже допустимую метку, вызовет исключение IndexError.

Атрибут .iloc является основным методом доступа. Допустимыми входными данными являются:

  • Целое число, например, 5
  • Список или массив целых чисел [4, 3, 0]
  • Объект среза с целыми числами 1:7
  • Массив булевых значений
  • callable, см. Выбор по вызываемому объекту
In [56]: s1 = pd.Series(np.random.randn(5), index=list(range(0,10,2)))

In [57]: s1
Out[57]: 
0    0.695775
2    0.341734
4    0.959726
6   -1.110336
8   -0.619976
dtype: float64

In [58]: s1.iloc[:3]
Out[58]: 
0    0.695775
2    0.341734
4    0.959726
dtype: float64

In [59]: s1.iloc[3]
Out[59]: -1.1103361028911669

Обратите внимание, что установка также работает:

In [60]: s1.iloc[:3] = 0

In [61]: s1
Out[61]: 
0    0.000000
2    0.000000
4    0.000000
6   -1.110336
8   -0.619976
dtype: float64

С DataFrame

In [62]: df1 = pd.DataFrame(np.random.randn(6,4),
   ....:                    index=list(range(0,12,2)),
   ....:                    columns=list(range(0,8,2)))
   ....: 

In [63]: df1
Out[63]: 
           0         2         4         6
0   0.149748 -0.732339  0.687738  0.176444
2   0.403310 -0.154951  0.301624 -2.179861
4  -1.369849 -0.954208  1.462696 -1.743161
6  -0.826591 -0.345352  1.314232  0.690579
8   0.995761  2.396780  0.014871  3.357427
10 -0.317441 -1.236269  0.896171 -0.487602

Выбор с помощью целочисленного среза

In [64]: df1.iloc[:3]
Out[64]: 
          0         2         4         6
0  0.149748 -0.732339  0.687738  0.176444
2  0.403310 -0.154951  0.301624 -2.179861
4 -1.369849 -0.954208  1.462696 -1.743161

In [65]: df1.iloc[1:5, 2:4]
Out[65]: 
          4         6
2  0.301624 -2.179861
4  1.462696 -1.743161
6  1.314232  0.690579
8  0.014871  3.357427

Выбор с помощью целочисленного списка

In [66]: df1.iloc[[1, 3, 5], [1, 3]]
Out[66]: 
           2         6
2  -0.154951 -2.179861
6  -0.345352  0.690579
10 -1.236269 -0.487602
In [67]: df1.iloc[1:3, :]
Out[67]: 
          0         2         4         6
2  0.403310 -0.154951  0.301624 -2.179861
4 -1.369849 -0.954208  1.462696 -1.743161
In [68]: df1.iloc[:, 1:3]
Out[68]: 
           2         4
0  -0.732339  0.687738
2  -0.154951  0.301624
4  -0.954208  1.462696
6  -0.345352  1.314232
8   2.396780  0.014871
10 -1.236269  0.896171
# this is also equivalent to ``df1.iat[1,1]``
In [69]: df1.iloc[1, 1]
Out[69]: -0.15495077442490321

Для получения поперечного сечения с помощью целочисленной позиции (эквивалентно df.xs(1))

In [70]: df1.iloc[1]
Out[70]: 
0    0.403310
2   -0.154951
4    0.301624
6   -2.179861
Name: 2, dtype: float64

Индексы срезов вне диапазона обрабатываются так же, как и в Python/Numpy.

# these are allowed in python/numpy.
# Only works in Pandas starting from v0.14.0.
In [71]: x = list('abcdef')

In [72]: x
Out[72]: ['a', 'b', 'c', 'd', 'e', 'f']

In [73]: x[4:10]
Out[73]: ['e', 'f']

In [74]: x[8:10]
Out[74]: []

In [75]: s = pd.Series(x)

In [76]: s
Out[76]: 
0    a
1    b
2    c
3    d
4    e
5    f
dtype: object

In [77]: s.iloc[4:10]
Out[77]: 
4    e
5    f
dtype: object

In [78]: s.iloc[8:10]
Out[78]: Series([], dtype: object)

Примечание

До версии v0.14.0, iloc не принимал индексаторы вне границ для срезов, например, значение, превышающее длину индексируемого объекта.

Обратите внимание, что это может привести к пустой оси (например, возвращается пустой DataFrame).

In [79]: dfl = pd.DataFrame(np.random.randn(5,2), columns=list('AB'))

In [80]: dfl
Out[80]: 
          A         B
0 -0.082240 -2.182937
1  0.380396  0.084844
2  0.432390  1.519970
3 -0.493662  0.600178
4  0.274230  0.132885

In [81]: dfl.iloc[:, 2:3]
Out[81]: 
Empty DataFrame
Columns: []
Index: [0, 1, 2, 3, 4]

In [82]: dfl.iloc[:, 1:3]
Out[82]: 
          B
0 -2.182937
1  0.084844
2  1.519970
3  0.600178
4  0.132885

In [83]: dfl.iloc[4:6]
Out[83]: 
         A         B
4  0.27423  0.132885

Один индексатор, находящийся вне границ, вызовет исключение IndexError. Список индексаторов, где любой элемент находится вне границ, вызовет исключение IndexError.

dfl.iloc[[4, 5, 6]]
IndexError: positional indexers are out-of-bounds

dfl.iloc[:, 4]
IndexError: single positional indexer is out-of-bounds

Выбор по вызываемому объекту

Новое в версии 0.18.1.

.loc, .iloc, .ix и также [] индексация могут принимать callable в качестве индексатора. callable должен быть функцией с одним аргументом (вызываемый Series, DataFrame или Panel), которая возвращает допустимый результат для индексации.

In [84]: df1 = pd.DataFrame(np.random.randn(6, 4),
   ....:                    index=list('abcdef'),
   ....:                    columns=list('ABCD'))
   ....: 

In [85]: df1
Out[85]: 
          A         B         C         D
a -0.023688  2.410179  1.450520  0.206053
b -0.251905 -2.213588  1.063327  1.266143
c  0.299368 -0.863838  0.408204 -1.048089
d -0.025747 -0.988387  0.094055  1.262731
e  1.289997  0.082423 -0.055758  0.536580
f -0.489682  0.369374 -0.034571 -2.484478

In [86]: df1.loc[lambda df: df.A > 0, :]
Out[86]: 
          A         B         C         D
c  0.299368 -0.863838  0.408204 -1.048089
e  1.289997  0.082423 -0.055758  0.536580

In [87]: df1.loc[:, lambda df: ['A', 'B']]
Out[87]: 
          A         B
a -0.023688  2.410179
b -0.251905 -2.213588
c  0.299368 -0.863838
d -0.025747 -0.988387
e  1.289997  0.082423
f -0.489682  0.369374

In [88]: df1.iloc[:, lambda df: [0, 1]]
Out[88]: 
          A         B
a -0.023688  2.410179
b -0.251905 -2.213588
c  0.299368 -0.863838
d -0.025747 -0.988387
e  1.289997  0.082423
f -0.489682  0.369374

In [89]: df1[lambda df: df.columns[0]]
Out[89]: 
a   -0.023688
b   -0.251905
c    0.299368
d   -0.025747
e    1.289997
f   -0.489682
Name: A, dtype: float64

Вы можете использовать индексацию вызываемого объекта в Series.

In [90]: df1.A.loc[lambda s: s > 0]
Out[90]: 
c    0.299368
e    1.289997
Name: A, dtype: float64

Используя эти методы/индексаторы, вы можете цепочкой операции выбора данных без использования временных переменных.

In [91]: bb = pd.read_csv('data/baseball.csv', index_col='id')

In [92]: (bb.groupby(['year', 'team']).sum()
   ....:    .loc[lambda df: df.r > 100])
   ....: 
Out[92]: 
           stint    g    ab    r    h  X2b  X3b  hr    rbi    sb   cs   bb  \
year team                                                                    
2007 CIN       6  379   745  101  203   35    2  36  125.0  10.0  1.0  105   
     DET       5  301  1062  162  283   54    4  37  144.0  24.0  7.0   97   
     HOU       4  311   926  109  218   47    6  14   77.0  10.0  4.0   60   
     LAN      11  413  1021  153  293   61    3  36  154.0   7.0  5.0  114   
     NYN      13  622  1854  240  509  101    3  61  243.0  22.0  4.0  174   
     SFN       5  482  1305  198  337   67    6  40  171.0  26.0  7.0  235   
     TEX       2  198   729  115  200   40    4  28  115.0  21.0  4.0   73   
     TOR       4  459  1408  187  378   96    2  58  223.0   4.0  2.0  190   

              so   ibb   hbp    sh    sf  gidp  
year team                                       
2007 CIN   127.0  14.0   1.0   1.0  15.0  18.0  
     DET   176.0   3.0  10.0   4.0   8.0  28.0  
     HOU   212.0   3.0   9.0  16.0   6.0  17.0  
     LAN   141.0   8.0   9.0   3.0   8.0  29.0  
     NYN   310.0  24.0  23.0  18.0  15.0  48.0  
     SFN   188.0  51.0   8.0  16.0   6.0  41.0  
     TEX   140.0   4.0   5.0   2.0   8.0  16.0  
     TOR   265.0  16.0  12.0   4.0  16.0  38.0  

Выбор случайных выборок

Случайный выбор строк или столбцов из Series, DataFrame или Panel с помощью метода sample(). Метод по умолчанию выбирает строки и принимает определенное количество строк/столбцов для возврата или долю строк.

In [93]: s = pd.Series([0,1,2,3,4,5])

# When no arguments are passed, returns 1 row.
In [94]: s.sample()
Out[94]: 
4    4
dtype: int64

# One may specify either a number of rows:
In [95]: s.sample(n=3)
Out[95]: 
0    0
4    4
1    1
dtype: int64

# Or a fraction of the rows:
In [96]: s.sample(frac=0.5)
Out[96]: 
5    5
3    3
1    1
dtype: int64

По умолчанию sample вернет каждую строку не более одного раза, но также можно выбрать с заменой с помощью опции replace:

In [97]: s = pd.Series([0,1,2,3,4,5])

 # Without replacement (default):
In [98]: s.sample(n=6, replace=False)
Out[98]: 
0    0
1    1
5    5
3    3
2    2
4    4
dtype: int64

 # With replacement:
In [99]: s.sample(n=6, replace=True)
Out[99]: 
0    0
4    4
3    3
2    2
4    4
4    4
dtype: int64

По умолчанию каждая строка имеет равную вероятность выбора, но если вы хотите, чтобы строки имели разные вероятности, вы можете передать функцию sample, взвешивающую выборку, как weights. Эти веса могут быть списком, массивом numpy или Series, но они должны иметь одинаковую длину с объектом, который вы выбираете. Пропущенные значения будут обрабатываться как вес 0, а значения inf не допускаются. Если веса не суммируются до 1, они будут перенормированы, разделив все веса на сумму весов. Например:

In [100]: s = pd.Series([0,1,2,3,4,5])

In [101]: example_weights = [0, 0, 0.2, 0.2, 0.2, 0.4]

In [102]: s.sample(n=3, weights=example_weights)
Out[102]: 
5    5
4    4
3    3
dtype: int64

# Weights will be re-normalized automatically
In [103]: example_weights2 = [0.5, 0, 0, 0, 0, 0]

In [104]: s.sample(n=1, weights=example_weights2)
Out[104]: 
0    0
dtype: int64

При применении к DataFrame вы можете использовать столбец DataFrame в качестве весов выборки (при условии, что вы выбираете строки, а не столбцы), просто передав имя столбца как строку.

In [105]: df2 = pd.DataFrame({'col1':[9,8,7,6], 'weight_column':[0.5, 0.4, 0.1, 0]})

In [106]: df2.sample(n = 3, weights = 'weight_column')
Out[106]: 
   col1  weight_column
1     8            0.4
0     9            0.5
2     7            0.1

sample также позволяет пользователям выбирать столбцы вместо строк с помощью аргумента axis.

In [107]: df3 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})

In [108]: df3.sample(n=1, axis=1)
Out[108]: 
   col1
0     1
1     2
2     3

Наконец, можно также установить семя генератора случайных чисел sample с помощью аргумента random_state, который будет принимать либо целое число (как семя), либо объект numpy RandomState.

In [109]: df4 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})

# With a given seed, the sample will always draw the same rows.
In [110]: df4.sample(n=2, random_state=2)
Out[110]: 
   col1  col2
2     3     4
1     2     3

In [111]: df4.sample(n=2, random_state=2)
Out[111]: 
   col1  col2
2     3     4
1     2     3

Установка с расширением

Новое в версии 0.13.

Операции .loc/.ix/[] могут выполнять расширение при установке несуществующего ключа для данной оси.

В случае Series это фактически операция добавления

In [112]: se = pd.Series([1,2,3])

In [113]: se
Out[113]: 
0    1
1    2
2    3
dtype: int64

In [114]: se[5] = 5.

In [115]: se
Out[115]: 
0    1.0
1    2.0
2    3.0
5    5.0
dtype: float64

DataFrame можно расширить по любой оси с помощью .loc

In [116]: dfi = pd.DataFrame(np.arange(6).reshape(3,2),
   .....:                 columns=['A','B'])
   .....: 

In [117]: dfi
Out[117]: 
   A  B
0  0  1
1  2  3
2  4  5

In [118]: dfi.loc[:,'C'] = dfi.loc[:,'A']

In [119]: dfi
Out[119]: 
   A  B  C
0  0  1  0
1  2  3  2
2  4  5  4

Это похоже на операцию append на DataFrame.

In [120]: dfi.loc[3] = 5

In [121]: dfi
Out[121]: 
   A  B  C
0  0  1  0
1  2  3  2
2  4  5  4
3  5  5  5

Быстрое получение и установка скалярных значений

Поскольку индексация с [] должна обрабатывать множество случаев (доступ к одному значению, срез, индексация булевых значений и т. д.), она имеет некоторую издержки для выяснения того, что вы запрашиваете. Если вам нужно получить только скалярное значение, самый быстрый способ - использовать методы at и iat, которые реализованы во всех структурах данных.

Аналогично loc, at обеспечивает метки скалярный поиск, в то время как iat обеспечивает целочисленный поиск аналогично iloc

In [122]: s.iat[5]
Out[122]: 5

In [123]: df.at[dates[5], 'A']
Out[123]: -0.67368970808837059

In [124]: df.iat[3, 0]
Out[124]: 0.72155516224436689

Вы также можете установить с использованием тех же индексаторов.

In [125]: df.at[dates[5], 'E'] = 7

In [126]: df.iat[3, 0] = 7

at может расширить объект на месте, как указано выше, если индексатор отсутствует.

In [127]: df.at[dates[-1]+1, 0] = 7

In [128]: df
Out[128]: 
                   A         B         C         D    E    0
2000-01-01  0.469112 -0.282863 -1.509059 -1.135632  NaN  NaN
2000-01-02  1.212112 -0.173215  0.119209 -1.044236  NaN  NaN
2000-01-03 -0.861849 -2.104569 -0.494929  1.071804  NaN  NaN
2000-01-04  7.000000 -0.706771 -1.039575  0.271860  NaN  NaN
2000-01-05 -0.424972  0.567020  0.276232 -1.087401  NaN  NaN
2000-01-06 -0.673690  0.113648 -1.478427  0.524988  7.0  NaN
2000-01-07  0.404705  0.577046 -1.715002 -1.039268  NaN  NaN
2000-01-08 -0.370647 -1.157892 -1.344312  0.844885  NaN  NaN
2000-01-09       NaN       NaN       NaN       NaN  NaN  7.0

Булевы индексы

Ещё одной распространённой операцией является использование булевых векторов для фильтрации данных. Операторы: | для or, & для and и ~ для not. Их необходимо группировать с помощью скобок.

Использование булевого вектора для индексации Series работает точно так же, как в numpy ndarray:

In [129]: s = pd.Series(range(-3, 4))

In [130]: s
Out[130]: 
0   -3
1   -2
2   -1
3    0
4    1
5    2
6    3
dtype: int64

In [131]: s[s > 0]
Out[131]: 
4    1
5    2
6    3
dtype: int64

In [132]: s[(s < -1) | (s > 0.5)]
Out[132]: 
0   -3
1   -2
4    1
5    2
6    3
dtype: int64

In [133]: s[~(s < 0)]
Out[133]: 
3    0
4    1
5    2
6    3
dtype: int64

Вы можете выбрать строки из DataFrame, используя булевый вектор такой же длины, как индекс DataFrame (например, что-то полученное из одного из столбцов DataFrame):

In [134]: df[df['A'] > 0]
Out[134]: 
                   A         B         C         D   E   0
2000-01-01  0.469112 -0.282863 -1.509059 -1.135632 NaN NaN
2000-01-02  1.212112 -0.173215  0.119209 -1.044236 NaN NaN
2000-01-04  7.000000 -0.706771 -1.039575  0.271860 NaN NaN
2000-01-07  0.404705  0.577046 -1.715002 -1.039268 NaN NaN

Для создания более сложных критериев также можно использовать списковые включения и метод map Series:

In [135]: df2 = pd.DataFrame({'a' : ['one', 'one', 'two', 'three', 'two', 'one', 'six'],
   .....:                     'b' : ['x', 'y', 'y', 'x', 'y', 'x', 'x'],
   .....:                     'c' : np.random.randn(7)})
   .....: 

# only want 'two' or 'three'
In [136]: criterion = df2['a'].map(lambda x: x.startswith('t'))

In [137]: df2[criterion]
Out[137]: 
       a  b         c
2    two  y  0.041290
3  three  x  0.361719
4    two  y -0.238075

# equivalent but slower
In [138]: df2[[x.startswith('t') for x in df2['a']]]
Out[138]: 
       a  b         c
2    two  y  0.041290
3  three  x  0.361719
4    two  y -0.238075

# Multiple criteria
In [139]: df2[criterion & (df2['b'] == 'x')]
Out[139]: 
       a  b         c
3  three  x  0.361719

Обратите внимание, что с помощью методов выбора Выбор по метке, Выбор по позиции и Расширенная индексация вы можете выбирать по нескольким осям, используя булевые векторы в сочетании с другими выражениями индексации.

In [140]: df2.loc[criterion & (df2['b'] == 'x'),'b':'c']
Out[140]: 
   b         c
3  x  0.361719

Индексация с помощью isin

Рассмотрим метод isin Series, который возвращает булевый вектор, равный true, где элементы Series существуют в переданном списке. Это позволяет вам выбирать строки, где один или несколько столбцов имеют значения, которые вы хотите:

In [141]: s = pd.Series(np.arange(5), index=np.arange(5)[::-1], dtype='int64')

In [142]: s
Out[142]: 
4    0
3    1
2    2
1    3
0    4
dtype: int64

In [143]: s.isin([2, 4, 6])
Out[143]: 
4    False
3    False
2     True
1    False
0     True
dtype: bool

In [144]: s[s.isin([2, 4, 6])]
Out[144]: 
2    2
0    4
dtype: int64

Тот же метод доступен для объектов Index и полезен в тех случаях, когда вы не знаете, какие из искомых меток на самом деле присутствуют:

In [145]: s[s.index.isin([2, 4, 6])]
Out[145]: 
4    0
2    2
dtype: int64

# compare it to the following
In [146]: s[[2, 4, 6]]
Out[146]: 
2    2.0
4    0.0
6    NaN
dtype: float64

Кроме того, MultiIndex позволяет выбрать отдельный уровень для использования в проверке принадлежности:

In [147]: s_mi = pd.Series(np.arange(6),
   .....:                  index=pd.MultiIndex.from_product([[0, 1], ['a', 'b', 'c']]))
   .....: 

In [148]: s_mi
Out[148]: 
0  a    0
   b    1
   c    2
1  a    3
   b    4
   c    5
dtype: int64

In [149]: s_mi.iloc[s_mi.index.isin([(1, 'a'), (2, 'b'), (0, 'c')])]
Out[149]: 
0  c    2
1  a    3
dtype: int64

In [150]: s_mi.iloc[s_mi.index.isin(['a', 'c', 'e'], level=1)]
Out[150]: 
0  a    0
   c    2
1  a    3
   c    5
dtype: int64

DataFrame также имеет метод isin. При вызове isin передайте набор значений в виде массива или словаря. Если values — массив, isin возвращает DataFrame булевых значений, имеющий такую же форму, что и исходный DataFrame, с True там, где элемент есть в последовательности значений.

In [151]: df = pd.DataFrame({'vals': [1, 2, 3, 4], 'ids': ['a', 'b', 'f', 'n'],
   .....:                    'ids2': ['a', 'n', 'c', 'n']})
   .....: 

In [152]: values = ['a', 'b', 1, 3]

In [153]: df.isin(values)
Out[153]: 
     ids   ids2   vals
0   True   True   True
1   True  False  False
2  False  False   True
3  False  False  False

Часто вы хотите сопоставить определённые значения с определёнными столбцами. Просто сделайте values словарем, где ключ — столбец, а значение — список элементов, которые вы хотите проверить.

In [154]: values = {'ids': ['a', 'b'], 'vals': [1, 3]}

In [155]: df.isin(values)
Out[155]: 
     ids   ids2   vals
0   True  False   True
1   True  False  False
2  False  False   True
3  False  False  False

Объедините метод isin DataFrame с методами any() и all(), чтобы быстро выбрать подмножества данных, удовлетворяющих заданным критериям. Чтобы выбрать строку, где каждый столбец удовлетворяет своему критерию:

In [156]: values = {'ids': ['a', 'b'], 'ids2': ['a', 'c'], 'vals': [1, 3]}

In [157]: row_mask = df.isin(values).all(1)

In [158]: df[row_mask]
Out[158]: 
  ids ids2  vals
0   a    a     1

Метод where() и маскирование

Выбор значений из Series с помощью булевого вектора обычно возвращает подмножество данных. Чтобы гарантировать, что результат выбора имеет ту же форму, что и исходные данные, вы можете использовать метод where в Series и DataFrame.

Чтобы вернуть только выбранные строки

In [159]: s[s > 0]
Out[159]: 
3    1
2    2
1    3
0    4
dtype: int64

Чтобы вернуть Series той же формы, что и исходная

In [160]: s.where(s > 0)
Out[160]: 
4    NaN
3    1.0
2    2.0
1    3.0
0    4.0
dtype: float64

Выбор значений из DataFrame с булевым критерием теперь также сохраняет форму входных данных. where используется в качестве реализации. Эквивалентно df.where(df < 0)

In [161]: df[df < 0]
Out[161]: 
                   A         B         C         D
2000-01-01 -2.104139 -1.309525       NaN       NaN
2000-01-02 -0.352480       NaN -1.192319       NaN
2000-01-03 -0.864883       NaN -0.227870       NaN
2000-01-04       NaN -1.222082       NaN -1.233203
2000-01-05       NaN -0.605656 -1.169184       NaN
2000-01-06       NaN -0.948458       NaN -0.684718
2000-01-07 -2.670153 -0.114722       NaN -0.048048
2000-01-08       NaN       NaN -0.048788 -0.808838

Кроме того, where принимает необязательный аргумент other для замены значений, где условие ложно, в возвращённой копии.

In [162]: df.where(df < 0, -df)
Out[162]: 
                   A         B         C         D
2000-01-01 -2.104139 -1.309525 -0.485855 -0.245166
2000-01-02 -0.352480 -0.390389 -1.192319 -1.655824
2000-01-03 -0.864883 -0.299674 -0.227870 -0.281059
2000-01-04 -0.846958 -1.222082 -0.600705 -1.233203
2000-01-05 -0.669692 -0.605656 -1.169184 -0.342416
2000-01-06 -0.868584 -0.948458 -2.297780 -0.684718
2000-01-07 -2.670153 -0.114722 -0.168904 -0.048048
2000-01-08 -0.801196 -1.392071 -0.048788 -0.808838

Вы можете установить значения на основе некоторых булевых критериев. Это можно сделать интуитивно следующим образом:

In [163]: s2 = s.copy()

In [164]: s2[s2 < 0] = 0

In [165]: s2
Out[165]: 
4    0
3    1
2    2
1    3
0    4
dtype: int64

In [166]: df2 = df.copy()

In [167]: df2[df2 < 0] = 0

In [168]: df2
Out[168]: 
                   A         B         C         D
2000-01-01  0.000000  0.000000  0.485855  0.245166
2000-01-02  0.000000  0.390389  0.000000  1.655824
2000-01-03  0.000000  0.299674  0.000000  0.281059
2000-01-04  0.846958  0.000000  0.600705  0.000000
2000-01-05  0.669692  0.000000  0.000000  0.342416
2000-01-06  0.868584  0.000000  2.297780  0.000000
2000-01-07  0.000000  0.000000  0.168904  0.000000
2000-01-08  0.801196  1.392071  0.000000  0.000000

По умолчанию where возвращает изменённую копию данных. Существует необязательный параметр inplace, чтобы исходные данные могли быть изменены без создания копии:

In [169]: df_orig = df.copy()

In [170]: df_orig.where(df > 0, -df, inplace=True);

In [171]: df_orig
Out[171]: 
                   A         B         C         D
2000-01-01  2.104139  1.309525  0.485855  0.245166
2000-01-02  0.352480  0.390389  1.192319  1.655824
2000-01-03  0.864883  0.299674  0.227870  0.281059
2000-01-04  0.846958  1.222082  0.600705  1.233203
2000-01-05  0.669692  0.605656  1.169184  0.342416
2000-01-06  0.868584  0.948458  2.297780  0.684718
2000-01-07  2.670153  0.114722  0.168904  0.048048
2000-01-08  0.801196  1.392071  0.048788  0.808838

Примечание

Подпись для DataFrame.where() отличается от numpy.where(). Примерно df1.where(m, df2) эквивалентно np.where(m, df1, df2).

In [172]: df.where(df < 0, -df) == np.where(df < 0, df, -df)
Out[172]: 
               A     B     C     D
2000-01-01  True  True  True  True
2000-01-02  True  True  True  True
2000-01-03  True  True  True  True
2000-01-04  True  True  True  True
2000-01-05  True  True  True  True
2000-01-06  True  True  True  True
2000-01-07  True  True  True  True
2000-01-08  True  True  True  True

выравнивание

Кроме того, where выравнивает входное булевое условие (ndarray или DataFrame), чтобы была возможна частичная выборка с установлением. Это аналогично частичной установке через .ix (но для содержимого, а не для меток осей)

In [173]: df2 = df.copy()

In [174]: df2[ df2[1:4] > 0 ] = 3

In [175]: df2
Out[175]: 
                   A         B         C         D
2000-01-01 -2.104139 -1.309525  0.485855  0.245166
2000-01-02 -0.352480  3.000000 -1.192319  3.000000
2000-01-03 -0.864883  3.000000 -0.227870  3.000000
2000-01-04  3.000000 -1.222082  3.000000 -1.233203
2000-01-05  0.669692 -0.605656 -1.169184  0.342416
2000-01-06  0.868584 -0.948458  2.297780 -0.684718
2000-01-07 -2.670153 -0.114722  0.168904 -0.048048
2000-01-08  0.801196  1.392071 -0.048788 -0.808838

Введено в версии 0.13.

Where также может принимать параметры axis и level для выравнивания ввода при выполнении where.

In [176]: df2 = df.copy()

In [177]: df2.where(df2>0,df2['A'],axis='index')
Out[177]: 
                   A         B         C         D
2000-01-01 -2.104139 -2.104139  0.485855  0.245166
2000-01-02 -0.352480  0.390389 -0.352480  1.655824
2000-01-03 -0.864883  0.299674 -0.864883  0.281059
2000-01-04  0.846958  0.846958  0.600705  0.846958
2000-01-05  0.669692  0.669692  0.669692  0.342416
2000-01-06  0.868584  0.868584  2.297780  0.868584
2000-01-07 -2.670153 -2.670153  0.168904 -2.670153
2000-01-08  0.801196  1.392071  0.801196  0.801196

Это эквивалентно (но быстрее), чем следующее.

In [178]: df2 = df.copy()

In [179]: df.apply(lambda x, y: x.where(x>0,y), y=df['A'])
Out[179]: 
                   A         B         C         D
2000-01-01 -2.104139 -2.104139  0.485855  0.245166
2000-01-02 -0.352480  0.390389 -0.352480  1.655824
2000-01-03 -0.864883  0.299674 -0.864883  0.281059
2000-01-04  0.846958  0.846958  0.600705  0.846958
2000-01-05  0.669692  0.669692  0.669692  0.342416
2000-01-06  0.868584  0.868584  2.297780  0.868584
2000-01-07 -2.670153 -2.670153  0.168904 -2.670153
2000-01-08  0.801196  1.392071  0.801196  0.801196

Введено в версии 0.18.1.

Where может принимать вызываемую функцию в качестве условия и аргументы other. Функция должна принимать один аргумент (вызываемую Series или DataFrame) и возвращать допустимый результат в качестве условия и аргумента other.

In [180]: df3 = pd.DataFrame({'A': [1, 2, 3],
   .....:                     'B': [4, 5, 6],
   .....:                     'C': [7, 8, 9]})
   .....: 

In [181]: df3.where(lambda x: x > 4, lambda x: x + 10)
Out[181]: 
    A   B  C
0  11  14  7
1  12   5  8
2  13   6  9

маска

mask — обратная булева операция для where.

In [182]: s.mask(s >= 0)
Out[182]: 
4   NaN
3   NaN
2   NaN
1   NaN
0   NaN
dtype: float64

In [183]: df.mask(df >= 0)
Out[183]: 
                   A         B         C         D
2000-01-01 -2.104139 -1.309525       NaN       NaN
2000-01-02 -0.352480       NaN -1.192319       NaN
2000-01-03 -0.864883       NaN -0.227870       NaN
2000-01-04       NaN -1.222082       NaN -1.233203
2000-01-05       NaN -0.605656 -1.169184       NaN
2000-01-06       NaN -0.948458       NaN -0.684718
2000-01-07 -2.670153 -0.114722       NaN -0.048048
2000-01-08       NaN       NaN -0.048788 -0.808838

Метод query() (Экспериментально)

Введено в версии 0.13.

DataFrame имеет метод query(), который позволяет выбирать с помощью выражения.

Вы можете получить значение кадра, где столбец b имеет значения между значениями столбцов a и c. Например:

In [184]: n = 10

In [185]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))

In [186]: df
Out[186]: 
          a         b         c
0  0.438921  0.118680  0.863670
1  0.138138  0.577363  0.686602
2  0.595307  0.564592  0.520630
3  0.913052  0.926075  0.616184
4  0.078718  0.854477  0.898725
5  0.076404  0.523211  0.591538
6  0.792342  0.216974  0.564056
7  0.397890  0.454131  0.915716
8  0.074315  0.437913  0.019794
9  0.559209  0.502065  0.026437

# pure python
In [187]: df[(df.a < df.b) & (df.b < df.c)]
Out[187]: 
          a         b         c
1  0.138138  0.577363  0.686602
4  0.078718  0.854477  0.898725
5  0.076404  0.523211  0.591538
7  0.397890  0.454131  0.915716

# query
In [188]: df.query('(a < b) & (b < c)')
Out[188]: 
          a         b         c
1  0.138138  0.577363  0.686602
4  0.078718  0.854477  0.898725
5  0.076404  0.523211  0.591538
7  0.397890  0.454131  0.915716

Сделайте то же самое, но вернитесь к именованному индексу, если столбец с именем a отсутствует.

In [189]: df = pd.DataFrame(np.random.randint(n / 2, size=(n, 2)), columns=list('bc'))

In [190]: df.index.name = 'a'

In [191]: df
Out[191]: 
   b  c
a      
0  0  4
1  0  1
2  3  4
3  4  3
4  1  4
5  0  3
6  0  1
7  3  4
8  2  3
9  1  1

In [192]: df.query('a < b and b < c')
Out[192]: 
   b  c
a      
2  3  4

Если вместо этого вы не хотите или не можете назвать свой индекс, вы можете использовать имя index в своём выражении запроса:

In [193]: df = pd.DataFrame(np.random.randint(n, size=(n, 2)), columns=list('bc'))

In [194]: df
Out[194]: 
   b  c
0  3  1
1  3  0
2  5  6
3  5  2
4  7  4
5  0  1
6  2  5
7  0  1
8  6  0
9  7  9

In [195]: df.query('index < b < c')
Out[195]: 
   b  c
2  5  6

Примечание

Если имя вашего индекса совпадает с именем столбца, приоритет отдаётся имени столбца. Например,

In [196]: df = pd.DataFrame({'a': np.random.randint(5, size=5)})

In [197]: df.index.name = 'a'

In [198]: df.query('a > 2') # uses the column 'a', not the index
Out[198]: 
   a
a   
1  3
3  3

Вы по-прежнему можете использовать индекс в выражении запроса, используя специальный идентификатор «index»:

In [199]: df.query('index > 2')
Out[199]: 
   a
a   
3  3
4  2

Если по какой-то причине у вас есть столбец под именем index, вы также можете сослаться на индекс как на ilevel_0, но на этом этапе вы должны подумать о переименовании своих столбцов на что-то менее неоднозначное.

MultiIndex query() Синтаксис

Вы также можете использовать уровни DataFrame с MultiIndex так, как будто они были столбцами в кадре:

In [200]: n = 10

In [201]: colors = np.random.choice(['red', 'green'], size=n)

In [202]: foods = np.random.choice(['eggs', 'ham'], size=n)

In [203]: colors
Out[203]: 
array(['red', 'red', 'red', 'green', 'green', 'green', 'green', 'green',
       'green', 'green'], 
      dtype='|S5')

In [204]: foods
Out[204]: 
array(['ham', 'ham', 'eggs', 'eggs', 'eggs', 'ham', 'ham', 'eggs', 'eggs',
       'eggs'], 
      dtype='|S4')

In [205]: index = pd.MultiIndex.from_arrays([colors, foods], names=['color', 'food'])

In [206]: df = pd.DataFrame(np.random.randn(n, 2), index=index)

In [207]: df
Out[207]: 
                   0         1
color food                    
red   ham   0.194889 -0.381994
      ham   0.318587  2.089075
      eggs -0.728293 -0.090255
green eggs -0.748199  1.318931
      eggs -2.029766  0.792652
      ham   0.461007 -0.542749
      ham  -0.305384 -0.479195
      eggs  0.095031 -0.270099
      eggs -0.707140 -0.773882
      eggs  0.229453  0.304418

In [208]: df.query('color == "red"')
Out[208]: 
                   0         1
color food                    
red   ham   0.194889 -0.381994
      ham   0.318587  2.089075
      eggs -0.728293 -0.090255

Если уровни MultiIndex не имеют имён, вы можете ссылаться на них, используя специальные имена:

In [209]: df.index.names = [None, None]

In [210]: df
Out[210]: 
                   0         1
red   ham   0.194889 -0.381994
      ham   0.318587  2.089075
      eggs -0.728293 -0.090255
green eggs -0.748199  1.318931
      eggs -2.029766  0.792652
      ham   0.461007 -0.542749
      ham  -0.305384 -0.479195
      eggs  0.095031 -0.270099
      eggs -0.707140 -0.773882
      eggs  0.229453  0.304418

In [211]: df.query('ilevel_0 == "red"')
Out[211]: 
                 0         1
red ham   0.194889 -0.381994
    ham   0.318587  2.089075
    eggs -0.728293 -0.090255

Конвенция — это ilevel_0, что означает «уровень индекса 0» для 0-го уровня index.

query() Сценарии использования

Сценарий использования query() — это когда у вас есть набор объектов DataFrame, у которых есть общее подмножество имён столбцов (или уровней/имён индексов). Вы можете передать один и тот же запрос в оба кадра без необходимости указывать, какой кадр вас интересует

In [212]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))

In [213]: df
Out[213]: 
          a         b         c
0  0.224283  0.736107  0.139168
1  0.302827  0.657803  0.713897
2  0.611185  0.136624  0.984960
3  0.195246  0.123436  0.627712
4  0.618673  0.371660  0.047902
5  0.480088  0.062993  0.185760
6  0.568018  0.483467  0.445289
7  0.309040  0.274580  0.587101
8  0.258993  0.477769  0.370255
9  0.550459  0.840870  0.304611

In [214]: df2 = pd.DataFrame(np.random.rand(n + 2, 3), columns=df.columns)

In [215]: df2
Out[215]: 
           a         b         c
0   0.357579  0.229800  0.596001
1   0.309059  0.957923  0.965663
2   0.123102  0.336914  0.318616
3   0.526506  0.323321  0.860813
4   0.518736  0.486514  0.384724
5   0.190804  0.505723  0.614533
6   0.891939  0.623977  0.676639
7   0.480559  0.378528  0.460858
8   0.420223  0.136404  0.141295
9   0.732206  0.419540  0.604675
10  0.604466  0.848974  0.896165
11  0.589168  0.920046  0.732716

In [216]: expr = '0.0 <= a <= c <= 0.5'

In [217]: map(lambda frame: frame.query(expr), [df, df2])
Out[217]: 
[          a         b         c
 8  0.258993  0.477769  0.370255,           a         b         c
 2  0.123102  0.336914  0.318616]

query() Сравнение синтаксиса Python и pandas

Полный синтаксис numpy

In [218]: df = pd.DataFrame(np.random.randint(n, size=(n, 3)), columns=list('abc'))

In [219]: df
Out[219]: 
   a  b  c
0  7  8  9
1  1  0  7
2  2  7  2
3  6  2  2
4  2  6  3
5  3  8  2
6  1  7  2
7  5  1  5
8  9  8  0
9  1  5  0

In [220]: df.query('(a < b) & (b < c)')
Out[220]: 
   a  b  c
0  7  8  9

In [221]: df[(df.a < df.b) & (df.b < df.c)]
Out[221]: 
   a  b  c
0  7  8  9

Несколько лучше, так как удаляются скобки (сравнительные операторы привязываются сильнее, чем &/|)

In [222]: df.query('a < b & b < c')
Out[222]: 
   a  b  c
0  7  8  9

Используйте английские слова вместо символов

In [223]: df.query('a < b and b < c')
Out[223]: 
   a  b  c
0  7  8  9

Довольно близко к тому, как вы можете написать это на бумаге

In [224]: df.query('a < b < c')
Out[224]: 
   a  b  c
0  7  8  9

Операторы in и not in

query() также поддерживает специальное использование операторов сравнения Python in и not in, предоставляя лаконичный синтаксис для вызова метода isin объекта Series или DataFrame.

# get all rows where columns "a" and "b" have overlapping values
In [225]: df = pd.DataFrame({'a': list('aabbccddeeff'), 'b': list('aaaabbbbcccc'),
   .....:                    'c': np.random.randint(5, size=12),
   .....:                    'd': np.random.randint(9, size=12)})
   .....: 

In [226]: df
Out[226]: 
    a  b  c  d
0   a  a  2  6
1   a  a  4  7
2   b  a  1  6
3   b  a  2  1
4   c  b  3  6
5   c  b  0  2
6   d  b  3  3
7   d  b  2  1
8   e  c  4  3
9   e  c  2  0
10  f  c  0  6
11  f  c  1  2

In [227]: df.query('a in b')
Out[227]: 
   a  b  c  d
0  a  a  2  6
1  a  a  4  7
2  b  a  1  6
3  b  a  2  1
4  c  b  3  6
5  c  b  0  2

# How you'd do it in pure Python
In [228]: df[df.a.isin(df.b)]
Out[228]: 
   a  b  c  d
0  a  a  2  6
1  a  a  4  7
2  b  a  1  6
3  b  a  2  1
4  c  b  3  6
5  c  b  0  2

In [229]: df.query('a not in b')
Out[229]: 
    a  b  c  d
6   d  b  3  3
7   d  b  2  1
8   e  c  4  3
9   e  c  2  0
10  f  c  0  6
11  f  c  1  2

# pure Python
In [230]: df[~df.a.isin(df.b)]
Out[230]: 
    a  b  c  d
6   d  b  3  3
7   d  b  2  1
8   e  c  4  3
9   e  c  2  0
10  f  c  0  6
11  f  c  1  2

Вы можете объединить это с другими выражениями для очень лаконичных запросов:

# rows where cols a and b have overlapping values and col c's values are less than col d's
In [231]: df.query('a in b and c < d')
Out[231]: 
   a  b  c  d
0  a  a  2  6
1  a  a  4  7
2  b  a  1  6
4  c  b  3  6
5  c  b  0  2

# pure Python
In [232]: df[df.b.isin(df.a) & (df.c < df.d)]
Out[232]: 
    a  b  c  d
0   a  a  2  6
1   a  a  4  7
2   b  a  1  6
4   c  b  3  6
5   c  b  0  2
10  f  c  0  6
11  f  c  1  2

Примечание

Обратите внимание, что in и not in вычисляются в Python, так как numexpr не имеет эквивалента этой операции. Однако только выражение in/not in само по себе вычисляется в обычном Python. Например, в выражении

df.query('a in b + c + d')

(b + c + d) вычисляется numexpr, а затем операция in вычисляется в обычном Python. В общем случае любые операции, которые можно вычислить с помощью numexpr, будут.

Специальное использование оператора == с объектами list

Сравнение списка значений со столбцом с помощью ==/!= работает аналогично in/not in

In [233]: df.query('b == ["a", "b", "c"]')
Out[233]: 
    a  b  c  d
0   a  a  2  6
1   a  a  4  7
2   b  a  1  6
3   b  a  2  1
4   c  b  3  6
5   c  b  0  2
6   d  b  3  3
7   d  b  2  1
8   e  c  4  3
9   e  c  2  0
10  f  c  0  6
11  f  c  1  2

# pure Python
In [234]: df[df.b.isin(["a", "b", "c"])]
Out[234]: 
    a  b  c  d
0   a  a  2  6
1   a  a  4  7
2   b  a  1  6
3   b  a  2  1
4   c  b  3  6
5   c  b  0  2
6   d  b  3  3
7   d  b  2  1
8   e  c  4  3
9   e  c  2  0
10  f  c  0  6
11  f  c  1  2

In [235]: df.query('c == [1, 2]')
Out[235]: 
    a  b  c  d
0   a  a  2  6
2   b  a  1  6
3   b  a  2  1
7   d  b  2  1
9   e  c  2  0
11  f  c  1  2

In [236]: df.query('c != [1, 2]')
Out[236]: 
    a  b  c  d
1   a  a  4  7
4   c  b  3  6
5   c  b  0  2
6   d  b  3  3
8   e  c  4  3
10  f  c  0  6

# using in/not in
In [237]: df.query('[1, 2] in c')
Out[237]: 
    a  b  c  d
0   a  a  2  6
2   b  a  1  6
3   b  a  2  1
7   d  b  2  1
9   e  c  2  0
11  f  c  1  2

In [238]: df.query('[1, 2] not in c')
Out[238]: 
    a  b  c  d
1   a  a  4  7
4   c  b  3  6
5   c  b  0  2
6   d  b  3  3
8   e  c  4  3
10  f  c  0  6

# pure Python
In [239]: df[df.c.isin([1, 2])]
Out[239]: 
    a  b  c  d
0   a  a  2  6
2   b  a  1  6
3   b  a  2  1
7   d  b  2  1
9   e  c  2  0
11  f  c  1  2

Булевы операторы

Вы можете инвертировать булевы выражения с помощью слова not или оператора ~.

In [240]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))

In [241]: df['bools'] = np.random.rand(len(df)) > 0.5

In [242]: df.query('~bools')
Out[242]: 
          a         b         c  bools
2  0.697753  0.212799  0.329209  False
7  0.275396  0.691034  0.826619  False
8  0.190649  0.558748  0.262467  False

In [243]: df.query('not bools')
Out[243]: 
          a         b         c  bools
2  0.697753  0.212799  0.329209  False
7  0.275396  0.691034  0.826619  False
8  0.190649  0.558748  0.262467  False

In [244]: df.query('not bools') == df[~df.bools]
Out[244]: 
      a     b     c bools
2  True  True  True  True
7  True  True  True  True
8  True  True  True  True

Конечно, выражения также могут быть сколь угодно сложными

# short query syntax
In [245]: shorter = df.query('a < b < c and (not bools) or bools > 2')

# equivalent in pure Python
In [246]: longer = df[(df.a < df.b) & (df.b < df.c) & (~df.bools) | (df.bools > 2)]

In [247]: shorter
Out[247]: 
          a         b         c  bools
7  0.275396  0.691034  0.826619  False

In [248]: longer
Out[248]: 
          a         b         c  bools
7  0.275396  0.691034  0.826619  False

In [249]: shorter == longer
Out[249]: 
      a     b     c bools
7  True  True  True  True

Производительность query()

DataFrame.query() с помощью numexpr немного быстрее, чем Python для больших кадров

_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 [250]: df2 = pd.DataFrame({'a': ['one', 'one', 'two', 'two', 'two', 'three', 'four'],
   .....:                     'b': ['x', 'y', 'x', 'y', 'x', 'x', 'x'],
   .....:                     'c': np.random.randn(7)})
   .....: 

In [251]: df2
Out[251]: 
       a  b         c
0    one  x -1.067137
1    one  y  0.309500
2    two  x -0.211056
3    two  y -1.842023
4    two  x -0.390820
5  three  x -1.964475
6   four  x  1.298329

In [252]: df2.duplicated('a')
Out[252]: 
0    False
1     True
2    False
3     True
4     True
5    False
6    False
dtype: bool

In [253]: df2.duplicated('a', keep='last')
Out[253]: 
0     True
1    False
2     True
3     True
4    False
5    False
6    False
dtype: bool

In [254]: df2.duplicated('a', keep=False)
Out[254]: 
0     True
1     True
2     True
3     True
4     True
5    False
6    False
dtype: bool

In [255]: df2.drop_duplicates('a')
Out[255]: 
       a  b         c
0    one  x -1.067137
2    two  x -0.211056
5  three  x -1.964475
6   four  x  1.298329

In [256]: df2.drop_duplicates('a', keep='last')
Out[256]: 
       a  b         c
1    one  y  0.309500
4    two  x -0.390820
5  three  x -1.964475
6   four  x  1.298329

In [257]: df2.drop_duplicates('a', keep=False)
Out[257]: 
       a  b         c
5  three  x -1.964475
6   four  x  1.298329

Также вы можете передать список столбцов для определения дубликатов.

In [258]: df2.duplicated(['a', 'b'])
Out[258]: 
0    False
1    False
2    False
3    False
4     True
5    False
6    False
dtype: bool

In [259]: df2.drop_duplicates(['a', 'b'])
Out[259]: 
       a  b         c
0    one  x -1.067137
1    one  y  0.309500
2    two  x -0.211056
3    two  y -1.842023
5  three  x -1.964475
6   four  x  1.298329

Чтобы удалить дубликаты по значению индекса, используйте Index.duplicated, а затем выполните срез. Те же параметры доступны в параметре keep.

In [260]: df3 = pd.DataFrame({'a': np.arange(6),
   .....:                     'b': np.random.randn(6)},
   .....:                    index=['a', 'a', 'b', 'c', 'b', 'a'])
   .....: 

In [261]: df3
Out[261]: 
   a         b
a  0  1.440455
a  1  2.456086
b  2  1.038402
c  3 -0.894409
b  4  0.683536
a  5  3.082764

In [262]: df3.index.duplicated()
Out[262]: array([False,  True, False, False,  True,  True], dtype=bool)

In [263]: df3[~df3.index.duplicated()]
Out[263]: 
   a         b
a  0  1.440455
b  2  1.038402
c  3 -0.894409

In [264]: df3[~df3.index.duplicated(keep='last')]
Out[264]: 
   a         b
c  3 -0.894409
b  4  0.683536
a  5  3.082764

In [265]: df3[~df3.index.duplicated(keep=False)]
Out[265]: 
   a         b
c  3 -0.894409

Метод get(), подобный словарю

У каждого из Series, DataFrame и Panel есть метод get, который может вернуть значение по умолчанию.

In [266]: s = pd.Series([1,2,3], index=['a','b','c'])

In [267]: s.get('a')               # equivalent to s['a']
Out[267]: 1

In [268]: s.get('x', default=-1)
Out[268]: -1

Метод select()

Еще один способ извлечения срезов из объекта — это метод select Series, DataFrame и Panel. Этот метод следует использовать только тогда, когда нет более прямого способа. select принимает функцию, которая обрабатывает метки по axis и возвращает булево значение. Например:

In [269]: df.select(lambda x: x == 'A', axis=1)
Out[269]: 
                   A
2000-01-01  0.355794
2000-01-02  1.635763
2000-01-03  0.854409
2000-01-04 -0.216659
2000-01-05  2.414688
2000-01-06 -1.206215
2000-01-07  0.779461
2000-01-08 -0.878999

Метод lookup()

Иногда вам нужно извлечь набор значений по заданной последовательности меток строк и столбцов, и метод lookup позволяет это сделать и возвращает массив NumPy. Например,

In [270]: dflookup = pd.DataFrame(np.random.rand(20,4), columns = ['A','B','C','D'])

In [271]: dflookup.lookup(list(range(0,10,2)), ['B','C','A','B','D'])
Out[271]: array([ 0.3506,  0.4779,  0.4825,  0.9197,  0.5019])

Объекты Index

Класс pandas Index и его подклассы можно рассматривать как реализующие упорядоченное множество. Дубликаты разрешены. Однако, если вы попытаетесь преобразовать объект Index с дублирующими записями в set, будет выброшено исключение.

Index также предоставляет инфраструктуру, необходимую для поиска, выравнивания данных и повторной индексации. Самый простой способ создать Index напрямую — это передать list или другую последовательность в Index:

In [272]: index = pd.Index(['e', 'd', 'a', 'b'])

In [273]: index
Out[273]: Index([u'e', u'd', u'a', u'b'], dtype='object')

In [274]: 'd' in index
Out[274]: True

Вы также можете передать name для хранения в индексе:

In [275]: index = pd.Index(['e', 'd', 'a', 'b'], name='something')

In [276]: index.name
Out[276]: 'something'

Имя, если оно задано, будет показано в отображении консоли:

In [277]: index = pd.Index(list(range(5)), name='rows')

In [278]: columns = pd.Index(['A', 'B', 'C'], name='cols')

In [279]: df = pd.DataFrame(np.random.randn(5, 3), index=index, columns=columns)

In [280]: df
Out[280]: 
cols         A         B         C
rows                              
0     1.295989  0.185778  0.436259
1     0.678101  0.311369 -0.528378
2    -0.674808 -1.103529 -0.656157
3     1.889957  2.076651 -1.102192
4    -1.211795 -0.791746  0.634724

In [281]: df['A']
Out[281]: 
rows
0    1.295989
1    0.678101
2   -0.674808
3    1.889957
4   -1.211795
Name: A, dtype: float64

Установка метаданных

Введено в версии 0.13.0.

Индексы «в основном неизменяемы», но можно устанавливать и изменять их метаданные, такие как индекс name (или для MultiIndex, levels и labels).

Вы можете использовать rename, set_names, set_levels и set_labels, чтобы установить эти атрибуты напрямую. По умолчанию они возвращают копию; однако, вы можете указать inplace=True, чтобы данные изменялись на месте.

См. Расширенная индексация для использования MultiIndexes.

In [282]: ind = pd.Index([1, 2, 3])

In [283]: ind.rename("apple")
Out[283]: Int64Index([1, 2, 3], dtype='int64', name=u'apple')

In [284]: ind
Out[284]: Int64Index([1, 2, 3], dtype='int64')

In [285]: ind.set_names(["apple"], inplace=True)

In [286]: ind.name = "bob"

In [287]: ind
Out[287]: Int64Index([1, 2, 3], dtype='int64', name=u'bob')

Введено в версии 0.15.0.

set_names, set_levels и set_labels также принимают необязательный аргумент level`

In [288]: index = pd.MultiIndex.from_product([range(3), ['one', 'two']], names=['first', 'second'])

In [289]: index
Out[289]: 
MultiIndex(levels=[[0, 1, 2], [u'one', u'two']],
           labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]],
           names=[u'first', u'second'])

In [290]: index.levels[1]
Out[290]: Index([u'one', u'two'], dtype='object', name=u'second')

In [291]: index.set_levels(["a", "b"], level=1)
Out[291]: 
MultiIndex(levels=[[0, 1, 2], [u'a', u'b']],
           labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]],
           names=[u'first', u'second'])

Операции над множествами с объектами Index

Предупреждение

В версии 0.15.0 операции над множествами + и - были устаревшими, чтобы предоставить их для операций с численными типами на определенных типах индексов. + можно заменить на .union() или |, а - на .difference().

Две основные операции — union (|), intersection (&). Их можно напрямую вызывать как методы экземпляра или использовать через перегруженные операторы. Разность предоставляется через метод .difference().

In [292]: a = pd.Index(['c', 'b', 'a'])

In [293]: b = pd.Index(['c', 'e', 'd'])

In [294]: a | b
Out[294]: Index([u'a', u'b', u'c', u'd', u'e'], dtype='object')

In [295]: a & b
Out[295]: Index([u'c'], dtype='object')

In [296]: a.difference(b)
Out[296]: Index([u'a', u'b'], dtype='object')

Также доступна операция symmetric_difference (^), которая возвращает элементы, присутствующие либо в idx1, либо в idx2, но не в обоих. Это эквивалентно индексу, созданному с помощью idx1.difference(idx2).union(idx2.difference(idx1)), с удаленными дубликатами.

In [297]: idx1 = pd.Index([1, 2, 3, 4])

In [298]: idx2 = pd.Index([2, 3, 4, 5])

In [299]: idx1.symmetric_difference(idx2)
Out[299]: Int64Index([1, 5], dtype='int64')

In [300]: idx1 ^ idx2
Out[300]: Int64Index([1, 5], dtype='int64')

Пропущенные значения

Введено в версии 0.17.1.

Важно

Хотя Index может содержать пропущенные значения (NaN), этого следует избегать, если вы не хотите получить неожиданные результаты. Например, некоторые операции неявно исключают пропущенные значения.

Index.fillna заполняет пропущенные значения заданным скалярным значением.

In [301]: idx1 = pd.Index([1, np.nan, 3, 4])

In [302]: idx1
Out[302]: Float64Index([1.0, nan, 3.0, 4.0], dtype='float64')

In [303]: idx1.fillna(2)
Out[303]: Float64Index([1.0, 2.0, 3.0, 4.0], dtype='float64')

In [304]: idx2 = pd.DatetimeIndex([pd.Timestamp('2011-01-01'), pd.NaT, pd.Timestamp('2011-01-03')])

In [305]: idx2
Out[305]: DatetimeIndex(['2011-01-01', 'NaT', '2011-01-03'], dtype='datetime64[ns]', freq=None)

In [306]: idx2.fillna(pd.Timestamp('2011-01-02'))
Out[306]: DatetimeIndex(['2011-01-01', '2011-01-02', '2011-01-03'], dtype='datetime64[ns]', freq=None)

Установка/Сброс индекса

Иногда вы загружаете или создаете набор данных в DataFrame и хотите добавить индекс после того, как уже сделали это. Есть несколько способов.

Установка индекса

DataFrame имеет метод set_index, который принимает имя столбца (для обычного Index) или список имен столбцов (для MultiIndex), чтобы создать новый индексированный DataFrame:

In [307]: data
Out[307]: 
     a    b  c    d
0  bar  one  z  1.0
1  bar  two  y  2.0
2  foo  one  x  3.0
3  foo  two  w  4.0

In [308]: indexed1 = data.set_index('c')

In [309]: indexed1
Out[309]: 
     a    b    d
c               
z  bar  one  1.0
y  bar  two  2.0
x  foo  one  3.0
w  foo  two  4.0

In [310]: indexed2 = data.set_index(['a', 'b'])

In [311]: indexed2
Out[311]: 
         c    d
a   b          
bar one  z  1.0
    two  y  2.0
foo one  x  3.0
    two  w  4.0

Ключевой параметр append позволяет сохранить существующий индекс и добавить заданные столбцы в MultiIndex:

In [312]: frame = data.set_index('c', drop=False)

In [313]: frame = frame.set_index(['a', 'b'], append=True)

In [314]: frame
Out[314]: 
           c    d
c a   b          
z bar one  z  1.0
y bar two  y  2.0
x foo one  x  3.0
w foo two  w  4.0

Другие параметры в set_index позволяют не удалять столбцы индекса или добавить индекс на месте (без создания нового объекта):

In [315]: data.set_index('c', drop=False)
Out[315]: 
     a    b  c    d
c                  
z  bar  one  z  1.0
y  bar  two  y  2.0
x  foo  one  x  3.0
w  foo  two  w  4.0

In [316]: data.set_index(['a', 'b'], inplace=True)

In [317]: data
Out[317]: 
         c    d
a   b          
bar one  z  1.0
    two  y  2.0
foo one  x  3.0
    two  w  4.0

Сброс индекса

Для удобства существует новая функция в DataFrame под названием reset_index, которая перемещает значения индекса в столбцы DataFrame и устанавливает простой целочисленный индекс. Это обратная операция к set_index

In [318]: data
Out[318]: 
         c    d
a   b          
bar one  z  1.0
    two  y  2.0
foo one  x  3.0
    two  w  4.0

In [319]: data.reset_index()
Out[319]: 
     a    b  c    d
0  bar  one  z  1.0
1  bar  two  y  2.0
2  foo  one  x  3.0
3  foo  two  w  4.0

Вывод более похож на таблицу SQL или массив записей. Имена столбцов, полученные из индекса, — это имена, хранящиеся в атрибуте names.

Вы можете использовать ключевое слово level, чтобы удалить только часть индекса:

In [320]: frame
Out[320]: 
           c    d
c a   b          
z bar one  z  1.0
y bar two  y  2.0
x foo one  x  3.0
w foo two  w  4.0

In [321]: frame.reset_index(level=1)
Out[321]: 
         a  c    d
c b               
z one  bar  z  1.0
y two  bar  y  2.0
x one  foo  x  3.0
w two  foo  w  4.0

reset_index принимает необязательный параметр drop, который, если он равен true, просто отбрасывает индекс, а не помещает значения индекса в столбцы DataFrame.

Примечание

Метод reset_index раньше назывался delevel, который теперь устарел.

Добавление произвольного индекса

Если вы создаете индекс самостоятельно, вы можете просто назначить его полю index:

data.index = index

Возврат представления или копии

При установке значений в объекте pandas необходимо учитывать, чтобы избежать того, что называется chained indexing. Вот пример.

In [322]: dfmi = pd.DataFrame([list('abcd'),
   .....:                      list('efgh'),
   .....:                      list('ijkl'),
   .....:                      list('mnop')],
   .....:                     columns=pd.MultiIndex.from_product([['one','two'],
   .....:                                                         ['first','second']]))
   .....: 

In [323]: dfmi
Out[323]: 
    one          two       
  first second first second
0     a      b     c      d
1     e      f     g      h
2     i      j     k      l
3     m      n     o      p

Сравните эти два метода доступа:

In [324]: dfmi['one']['second']
Out[324]: 
0    b
1    f
2    j
3    n
Name: second, dtype: object
In [325]: dfmi.loc[:,('one','second')]
Out[325]: 
0    b
1    f
2    j
3    n
Name: (one, second), dtype: object

Они оба дают одинаковые результаты, так какой же следует использовать? Полезно понять порядок операций с ними и почему метод 2 (.loc) намного предпочтительнее метода 1 (цепочечная []).

dfmi['one'] выбирает первый уровень столбцов и возвращает DataFrame, который имеет одноуровневый индекс. Затем другая операция Python dfmi_with_one['second'] выбирает ряд, индексированный по 'second'. Это показано переменной dfmi_with_one, потому что pandas рассматривает эти операции как отдельные события. Например, отдельные вызовы __getitem__, поэтому он должен рассматривать их как линейные операции, которые происходят одна за другой.

В отличие от df.loc[:,('one','second')], который передает вложенную кортеж из (slice(None),('one','second')) в один вызов __getitem__. Это позволяет pandas обрабатывать это как единое целое. Кроме того, такой порядок операций может быть значительно быстрее и позволяет индексировать обе оси, если это необходимо.

Почему присваивание не работает при использовании цепочечной индексации?

Проблема в предыдущем разделе — просто вопрос производительности. Что случилось с предупреждением SettingWithCopy? Мы обычно не выводим предупреждения, когда вы делаете что-то, что может стоить несколько дополнительных миллисекунд!

Но оказывается, что присвоение результату цепочечной индексации имеет неопределенные результаты. Чтобы увидеть это, подумайте о том, как интерпретатор Python выполняет этот код:

dfmi.loc[:,('one','second')] = value
# becomes
dfmi.loc.__setitem__((slice(None), ('one', 'second')), value)

Но этот код обрабатывается по-другому:

dfmi['one']['second'] = value
# becomes
dfmi.__getitem__('one').__setitem__('second', value)

Видите __getitem__ там? За пределами простых случаев очень сложно предсказать, вернет ли он представление или копию (это зависит от расположения массива в памяти, в отношении которого pandas не дает никаких гарантий), и, следовательно, изменит ли __setitem__ dfmi или временный объект, который выбрасывается сразу же после этого. Это то, о чем предупреждает SettingWithCopy!

Примечание

Возможно, вы задаётесь вопросом, стоит ли беспокоиться о свойстве loc в первом примере. Но dfmi.loc гарантированно является dfmi с изменённым поведением индексирования, поэтому dfmi.loc.__getitem__ / dfmi.loc.__setitem__ работают непосредственно с dfmi. Конечно, dfmi.loc.__getitem__(idx) может быть представлением или копией dfmi.

Иногда возникает предупреждение SettingWithCopy в случаях, когда нет очевидного цепочечного индексирования. Эти ошибки — те, которые SettingWithCopy предназначен для обнаружения! Pandas, вероятно, пытается предупредить вас о том, что вы сделали это:

def do_something(df):
   foo = df[['bar', 'baz']]  # Is foo a view? A copy? Nobody knows!
   # ... many lines here ...
   foo['quux'] = value       # We don't know whether this will modify df or not!
   return foo

Упс!

Порядок вычислений имеет значение

Кроме того, в цепочечных выражениях порядок может определить, возвращается ли копия или нет. Если выражение будет устанавливать значения в копии среза, будет возбуждено исключение SettingWithCopy (это поведение подъёма/предупреждения введено начиная с 0.13.0).

Вы можете контролировать действие цепочечной присваивания с помощью опции mode.chained_assignment, которая может принимать значения ['raise','warn',None], где показ предупреждения является значением по умолчанию.

In [326]: dfb = pd.DataFrame({'a' : ['one', 'one', 'two',
   .....:                            'three', 'two', 'one', 'six'],
   .....:                     'c' : np.arange(7)})
   .....: 

# This will show the SettingWithCopyWarning
# but the frame values will be set
In [327]: dfb['c'][dfb.a.str.startswith('o')] = 42

Однако это действует на копию и не сработает.

>>> pd.set_option('mode.chained_assignment','warn')
>>> dfb[dfb.a.str.startswith('o')]['c'] = 42
Traceback (most recent call last)
     ...
SettingWithCopyWarning:
     A value is trying to be set on a copy of a slice from a DataFrame.
     Try using .loc[row_index,col_indexer] = value instead

Цепочечная присваивания также может возникнуть при настройке в фрейме смешанного типа.

Примечание

Эти правила настройки применяются ко всем .loc/.iloc/.ix

Это правильный метод доступа

In [328]: dfc = pd.DataFrame({'A':['aaa','bbb','ccc'],'B':[1,2,3]})

In [329]: dfc.loc[0,'A'] = 11

In [330]: dfc
Out[330]: 
     A  B
0   11  1
1  bbb  2
2  ccc  3

Это может сработать в некоторых случаях, но не гарантируется, и поэтому следует избегать.

In [331]: dfc = dfc.copy()

In [332]: dfc['A'][0] = 111

In [333]: dfc
Out[333]: 
     A  B
0  111  1
1  bbb  2
2  ccc  3

Это не сработает вообще и поэтому следует избегать.

>>> pd.set_option('mode.chained_assignment','raise')
>>> dfc.loc[0]['A'] = 1111
Traceback (most recent call last)
     ...
SettingWithCopyException:
     A value is trying to be set on a copy of a slice from a DataFrame.
     Try using .loc[row_index,col_indexer] = value instead

Предупреждение

Предупреждения/исключения при цепочечной присваивания предназначены для информирования пользователя о возможном некорректном присваивании. Могут быть ложноположительные срабатывания; ситуации, когда цепочечная присваивания ошибочно сообщается.

© 2008–2012, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/0.19.2/indexing.html

Spec-Zone.ru

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