Рассчитываются ли результаты метода numpy.where на фрейме данных pandas для всего массива или отфильтрованного массива? - PullRequest
1 голос
/ 25 июня 2019

Я хочу использовать numpyp.where на фрейме данных pandas, чтобы проверить наличие определенной строки в столбце. Если строка присутствует, примените функцию split и возьмите второй элемент списка, если не просто первый символ. Однако следующий код не работает, он выдает IndexError: list index out of range, потому что первая запись не содержит подчеркивания:

import pandas as pd
import numpy as np
df = pd.DataFrame({'A':['a','a_1','b_','b_2_3']})
df["B"] = np.where(df.A.str.contains('_'),df.A.apply(lambda x: x.split('_')[1]),df.A.str[0])

Только вызов np.where возвращает массив индексов, для которых условие выполняется, поэтому у меня сложилось впечатление, что команда split будет использоваться только для этого подмножества данных:

np.where(df.A.str.contains('_'))
Out[14]: (array([1, 2, 3], dtype=int64),)

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

Я не прошу альтернативного решения, придумать, что не сложно.

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

Ответы [ 2 ]

1 голос
/ 25 июня 2019

Python не является "ленивым" языком , поэтому код оценивается немедленно.генераторы / итераторы действительно вводят некоторую лень, но это не применимо здесь

, если мы разделим вашу строку кода, мы получим следующие утверждения:

  1. df.A.str.contains('_')
  2. df.A.apply(lambda x: x.split('_')[1])
  3. df.A.str[0]

Python должен оценить эти операторы, прежде чем он сможет передать их в качестве аргументов np.where

, чтобы увидеть всеВ этом случае мы можем переписать вышеприведенные маленькие функции, которые отображают некоторые выходные данные:

def fn_contains(x):
    print('contains', x)
    return '_' in x

def fn_split(x):
    s = x.split('_')
    print('split', x, s)
    # check for errors here
    if len(s) > 1:
        return s[1]

def fn_first(x):
    print('first', x)
    return x[0]

, а затем вы можете запустить их на своих данных с помощью:

s = pd.Series(['a','a_1','b_','b_2_3'])
np.where(
  s.apply(fn_contains),
  s.apply(fn_split),
  s.apply(fn_first)
)

, и вы увидитевсе выполняется по порядку.это в основном то, что происходит "внутри" numpy / pandas, когда вы выполняете вещи

1 голос
/ 25 июня 2019

По моему мнению, numpy.where только устанавливает значения по условию, поэтому второй и третий массивы учитываются для всех данных - отфильтрованных и не отфильтрованных.

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

mask = df.A.str.contains('_')
df.loc[mask, "B"] = df.loc[mask, "A"].str.split('_').str[1]

В вашем решении есть ошибка, но проблема не связана с np.where. После разделения на _, если не существует значения, получите один список элементов, поэтому при выборе второго значения списка на [1] выведите ошибку:

print (df.A.apply(lambda x: x.split('_')))
0          [a]
1       [a, 1]
2        [b, ]
3    [b, 2, 3]
Name: A, dtype: object

print (df.A.apply(lambda x: x.split('_')[1]))
IndexError: list index out of range

Так что здесь можно использовать решение pandas, если производительность не важна, потому что строковые функции работают медленно:

df["B"] = np.where(df.A.str.contains('_'), 
                   df.A.str.split('_').str[1],
                   df.A.str[0])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...