Сообщить об ошибке.

Методы .mask() и .where() объектов Series/DataFrame в pandas

Применение идиомы if/then к элементам Series/DataFrame в pandas

Методы .mask() и .where() являются применением идиомы if/then. К тому же, выбор значений из ряда с логическим вектором обычно возвращает подмножество данных Series/DataFrame. Чтобы гарантировать, что выходные данные выборки имеют ту же форму, что и исходные данные, можно использовать методы .mask() и .where() в Series и DataFrame.

Содержание:


- DataFrame.mask(cond, other=no_default, *, inplace=False, axis=None, level=None):
- Series.mask(cond, other=no_default, *, inplace=False, level=None):

Методы DataFrame.mask() и Series.mask() заменяют значения элементов соответствующих объектов, в которых условие равно True.

Эти методы являются применением идиомы if/then. Для каждого элемента в вызывающем DataFrame/Series, если cond равно False, то используется его элемент, в противном случае используется соответствующий элемент из other. Если ось other не совпадает с осью cond DataFrame/Series, то смещенные позиции индекса будут заполнены значением True.

Принимаемые аргументы:

  • cond - там, где cond имеет значение False, сохраняется исходное значение. Если True, то заменяет соответствующим значением из other. Если cond является вызываемым объектом, то он вычисляется в Series/DataFrame и должен возвращать логический Series/DataFrame или массив. Вызываемый объект не должен изменять входные Series/DataFrame (хотя pandas это не проверяет).
  • other - там, где cond имеет значение True, заменяются соответствующим значением из other. Если other является вызываемым объектом, он вычисляется в Series/DataFrame и должен возвращать скаляр или Series/DataFrame. Вызываемый объект не должен изменять входные Series/DataFrame (хотя pandas не проверяет это). Если не указан, то записи будут заполнены соответствующим значением NULL (np.nan для dtypes из numpy , pd.NA для расширений dtypes).
  • inplace=False - следует ли выполнять операцию на месте с данными (не рекомендуется к использованию).
  • axis=None - при необходимости выравнивает данные по указанной оси. Для Series этот аргумент не используется и по умолчанию равен 0.
  • level=None - уровень выравнивания при необходимости

- DataFrame.where(cond, other=nan, *, inplace=False, axis=None, level=None):
- Series.where(cond, other=nan, *, inplace=False level=None):

Методы DataFrame.where() и Series.where() заменяют значения элементов соответствующих объектов, в которых условие равно False.

Эти методы являются применением идиомы if/then. Для каждого элемента в вызывающем DataFrame/Series, если cond равно True, то используется его элемент, в противном случае используется соответствующий элемент из other. Если ось other не совпадает с осью cond DataFrame/Series, то смещенные позиции индекса будут заполнены значением True.

Принимаемые аргументы:

  • cond - там, где cond имеет значение False, сохраняется исходное значение. Если True, то заменяет соответствующим значением из other. Если cond является вызываемым объектом, то он вычисляется в Series/DataFrame и должен возвращать логический Series/DataFrame или массив. Вызываемый объект не должен изменять входные Series/DataFrame (хотя pandas это не проверяет).
  • other - там, где cond имеет значение True, заменяются соответствующим значением из other. Если other является вызываемым объектом, он вычисляется в Series/DataFrame и должен возвращать скаляр или Series/DataFrame. Вызываемый объект не должен изменять входные Series/DataFrame (хотя pandas не проверяет это). Если не указан, то записи будут заполнены соответствующим значением NULL (np.nan для dtypes из numpy , pd.NA для расширений dtypes).
  • inplace=False - следует ли выполнять операцию на месте с данными (не рекомендуется к использованию).
  • axis=None - при необходимости выравнивает данные по указанной оси. Для Series этот аргумент не используется и по умолчанию равен 0.
  • level=None - уровень выравнивания при необходимости

Сигнатура для DataFrame.where() отличается от numpy.where(). Грубо df1.where(m, df2) эквивалентен np.where(m, df1, df2).

Примеры использования методов .mask() и .where() объектов Series/DataFrame

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

Чтобы вернуть только выбранные строки, нужно выполнить следующие действия.

>>> import pandas as pd
>>> import numpy as np
>>> s = pd.Series(np.arange(5), index=np.arange(5)[::-1], dtype='int64')
>>> s
# 4    0
# 3    1
# 2    2
# 1    3
# 0    4
# dtype: int64

>>> s[s > 0]
# 3    1
# 2    2
# 1    3
# 0    4
# dtype: int64

Чтобы вернуть серию той же формы, что и оригинал:

>>> s.where(s > 0)
# 4    NaN
# 3    1.0
# 2    2.0
# 1    3.0
# 0    4.0
# dtype: float64

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

>>> dates = pd.date_range('1/1/2000', periods=8)

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

# выбор данных с помощью логического вектора
>>> df[df < 0]
#                    A         B         C         D
# 2000-01-01 -0.044333 -0.051989 -1.947218 -0.677572
# 2000-01-02 -0.631469       NaN -1.573849 -0.853425
# 2000-01-03       NaN -0.272759 -0.577754       NaN
# 2000-01-04       NaN       NaN -0.179180 -0.147158
# 2000-01-05 -0.339024 -0.990385       NaN       NaN
# 2000-01-06       NaN -0.297956       NaN       NaN
# 2000-01-07 -0.131227       NaN       NaN -1.202555
# 2000-01-08       NaN -1.957476       NaN -0.409341

# выбор данных с помощью метода `.where()`
>>> df.where(df < 0)
#                    A         B         C         D
# 2000-01-01 -0.044333 -0.051989 -1.947218 -0.677572
# 2000-01-02 -0.631469       NaN -1.573849 -0.853425
# 2000-01-03       NaN -0.272759 -0.577754       NaN
# 2000-01-04       NaN       NaN -0.179180 -0.147158
# 2000-01-05 -0.339024 -0.990385       NaN       NaN
# 2000-01-06       NaN -0.297956       NaN       NaN
# 2000-01-07 -0.131227       NaN       NaN -1.202555
# 2000-01-08       NaN -1.957476       NaN -0.409341

Кроме того, метод .where() принимает необязательный аргумент other для замены значений, если условие cond имеет значение False, в возвращаемой копии. Метод .mask() работает аналогичным образом, только заменяет значения, если условие cond имеет значение True

>>> df.where(df < 0, -df)
#                    A         B         C         D
# 2000-01-01 -0.044333 -0.051989 -1.947218 -0.677572
# 2000-01-02 -0.631469 -2.272832 -1.573849 -0.853425
# 2000-01-03 -0.366391 -0.272759 -0.577754 -0.719210
# 2000-01-04 -1.804667 -0.363212 -0.179180 -0.147158
# 2000-01-05 -0.339024 -0.990385 -0.399724 -0.832043
# 2000-01-06 -0.588127 -0.297956 -1.030587 -0.976066
# 2000-01-07 -0.131227 -0.967801 -0.545073 -1.202555
# 2000-01-08 -0.185359 -1.957476 -1.129694 -0.409341

# эквивалентно
>>> df.mask(df > 0, -df)
#                    A         B         C         D
# 2000-01-01 -0.044333 -0.051989 -1.947218 -0.677572
# 2000-01-02 -0.631469 -2.272832 -1.573849 -0.853425
# 2000-01-03 -0.366391 -0.272759 -0.577754 -0.719210
# 2000-01-04 -1.804667 -0.363212 -0.179180 -0.147158
# 2000-01-05 -0.339024 -0.990385 -0.399724 -0.832043
# 2000-01-06 -0.588127 -0.297956 -1.030587 -0.976066
# 2000-01-07 -0.131227 -0.967801 -0.545073 -1.202555
# 2000-01-08 -0.185359 -1.957476 -1.129694 -0.409341

Методы .where() и .mask() также могут принимать аргументы оси axis и уровня level для выравнивания входных данных при выполнении.

>>> df.where(df > 0, df['A'], axis='index')
#                    A         B         C         D
# 2000-01-01 -0.044333 -0.044333 -0.044333 -0.044333
# 2000-01-02 -0.631469  2.272832 -0.631469 -0.631469
# 2000-01-03  0.366391  0.366391  0.366391  0.719210
# 2000-01-04  1.804667  0.363212  1.804667  1.804667
# 2000-01-05 -0.339024 -0.339024  0.399724  0.832043
# 2000-01-06  0.588127  0.588127  1.030587  0.976066
# 2000-01-07 -0.131227  0.967801  0.545073 -0.131227
# 2000-01-08  0.185359  0.185359  1.129694  0.185359

# код выше эквивалентен (но быстрее) следующего:
>>> df.apply(lambda x, y: x.where(x > 0, y), y=df['A'])
#                    A         B         C         D
# 2000-01-01 -0.044333 -0.044333 -0.044333 -0.044333
# 2000-01-02 -0.631469  2.272832 -0.631469 -0.631469
# 2000-01-03  0.366391  0.366391  0.366391  0.719210
# 2000-01-04  1.804667  0.363212  1.804667  1.804667
# 2000-01-05 -0.339024 -0.339024  0.399724  0.832043
# 2000-01-06  0.588127  0.588127  1.030587  0.976066
# 2000-01-07 -0.131227  0.967801  0.545073 -0.131227
# 2000-01-08  0.185359  0.185359  1.129694  0.185359

Методы .where() и .mask() могут принимать вызываемый объект в качестве условия cond и аргумента other. Функция должна принимать один аргумент (вызывающий Series или DataFrame) и возвращать допустимые выходные данные.

>>> tmp = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]})
>>> tmp.where(lambda x: x > 4, lambda x: x + 10)
#     A   B  C
# 0  11  14  7
# 1  12   5  8
# 2  13   6  9

Еще примеры (без комментариев)

>>> s = pd.Series(range(5))
>>> s.where(s > 0)
# 0    NaN
# 1    1.0
# 2    2.0
# 3    3.0
# 4    4.0
# dtype: float64

>>> s.mask(s > 0)
# 0    0.0
# 1    NaN
# 2    NaN
# 3    NaN
# 4    NaN
# dtype: float64
>>> s = pd.Series(range(5))
>>> t = pd.Series([True, False])
>>> s.where(t, 99)
# 0     0
# 1    99
# 2    99
# 3    99
# 4    99
# dtype: int64

>>> s.mask(t, 99)
# 0    99
# 1     1
# 2    99
# 3    99
# 4    99
# dtype: int64
>>> s.where(s > 1, 10)
# 0    10
# 1    10
# 2    2
# 3    3
# 4    4
# dtype: int64

>>> s.mask(s > 1, 10)
# 0     0
# 1     1
# 2    10
# 3    10
# 4    10
# dtype: int64
>>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B'])
>>> df
#    A  B
# 0  0  1
# 1  2  3
# 2  4  5
# 3  6  7
# 4  8  9

>>> m = df % 3 == 0
>>> df.where(m, -df)
#    A  B
# 0  0 -1
# 1 -2  3
# 2 -4 -5
# 3  6 -7
# 4 -8  9

>>> df.where(m, -df) == np.where(m, df, -df)
#       A     B
# 0  True  True
# 1  True  True
# 2  True  True
# 3  True  True
# 4  True  True

>>> df.where(m, -df) == df.mask(~m, -df)
#       A     B
# 0  True  True
# 1  True  True
# 2  True  True
# 3  True  True
# 4  True  True