Маскирование панд DataFrame с массивом массивов против DataFrame - PullRequest
0 голосов
/ 01 сентября 2018

Я хочу использовать двумерную логическую маску для выборочного изменения некоторых ячеек в pandas DataFrame. Я заметил, что я не могу использовать массив numpy (успешно) в качестве маски, но я могу использовать DataFrame. Однако более расстраивает то, что я не получаю ошибку при numpy подходе .

Например,

df = pd.DataFrame({'A':[1,2,3,4], 
                   'B':[10,20,30,40]})

mask_np = np.array([[True,True],
                    [False,False],
                    [True,False],
                    [False,True]])

mask_pd = pd.DataFrame(mask_np, columns=['A','B'])

Я думаю, что любая маска будет возвращать значения из df, где бы маска не была True. Но вместо этого df[mask_np] производит

   A   B
0  1  10
0  1  10
2  3  30
3  4  40

это не то, что я ожидаю, и я не могу объяснить. С другой стороны, df[mask_pd] производит

     A     B
0  1.0  10.0
1  NaN   NaN
2  3.0   NaN
3  NaN  40.0

это то, чего я ожидаю и хочу.

Почему я не могу использовать маску numpy? Мой поиск в интернете ничего не дал. Любое объяснение этой разницы будет с благодарностью!

[pandas версия 0.20.3; Python 3.6.3]

Ответы [ 2 ]

0 голосов
/ 01 сентября 2018

Запишите индексы строк True в вашем mask_np: строка 0, строка 0, строка 2, строка 3. Выберите строки с одинаковыми индексами в df и объедините их. Вот так получается df[mask_np].

Вероятно, это ошибка Pandas, поскольку в исходном коде предполагается, что массив, используемый для индексации, является одномерным.


Глядя на исходный код (Pandas 0.23.4),

df[mask_np]

эквивалентно

df._getitem_bool_array(mask_np)

эквивалентно

indexer = mask_np.nonzero()[0]
df._take(indexer, axis=0)

со следующей оценкой:

>>> mask_np.nonzero()
(array([0, 0, 2, 3]), array([0, 1, 0, 1]))

Этот набор массивов представляет индексы ненулевых элементов по размерам массива. В этом случае элементы первого массива в кортеже (в конечном итоге используемые в df._take) - это индексы 'row' из True в mask_df.

Первый массив используется для take вдоль индекса, поэтому в ответ вы получите строки 0, 0, 2, 3 из df.

0 голосов
/ 01 сентября 2018

Исходный код подсказывает почему. Метод __getitem__, для которого [] является синтаксическим сахаром, проверяет специально для индексации через фрейм данных:

elif isinstance(key, DataFrame):
    return self._getitem_frame(key)

Вызванный метод _getitem_frame возвращает pd.DataFrame.where, если информационный кадр имеет логический тип:

def _getitem_frame(self, key):
    if key.values.size and not is_bool_dtype(key.values):
        raise ValueError('Must pass DataFrame with boolean values only')
    return self.where(key)

Маршрут, выбранный для массивов NumPy, _getitem_array, отличается и более запутан. По какой-то причине код предназначен для обработки входов NumPy / Pandas по-разному, а не для обеспечения согласованности для одних и тех же типов данных .


Регулярное логическое индексирование с помощью фрейма данных Pandas обычно применяется вдоль оси, то есть по строкам / оси 0 через df.loc[mask, :] или по столбцам / оси 1 через df.loc[:, mask].

Обратите внимание, что вы можете и, вероятно, должны получить доступ к pd.DataFrame.where напрямую для ясности:

res = df.where(mask_np)

print(res)

     A     B
0  1.0  10.0
1  NaN   NaN
2  3.0   NaN
3  NaN  40.0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...