it-roy-ru.com

Как удалить строки из pandas DataFrame на основе условного выражения

У меня есть pandas DataFrame, и я хочу удалить из него строки, у которых длина строки в конкретном столбце больше 2. Я знаю, что могу использовать df.dropna(), чтобы избавиться от строк, содержащих любые NaN, но я не вижу, как удалить строки на основе условного выражения.

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

df[(len(df['column name']) < 2)]

но я просто получаю ошибку:

KeyError: u'no item named False'

Может кто-нибудь сказать мне, что я делаю не так?

210
sjs

Когда вы выполняете функцию len(df['column name']), вы просто получаете одно число, а именно число строк в кадре данных (то есть длину самого столбца). Если вы хотите применить len к каждому элементу в столбце, используйте df['column name'].map(len). Так что постарайтесь

df[df['column name'].map(len) < 2]
96
BrenBarn

Чтобы напрямую ответить на заголовок этого вопроса (который, как я понимаю, не обязательно является проблемой ОП, но может помочь другим пользователям, сталкивающимся с этим вопросом), один из способов сделать это - использовать метод drop :

df = df.drop(some labels)

df = df.drop(df[<some boolean condition>].index)

Пример

Чтобы удалить все строки, в которых столбец «Score» <50:

df = df.drop(df[df.score < 50].index)

Версия на месте (как указано в комментариях)

df.drop(df[df.score < 50].index, inplace=True)

Несколько условий

(см. Булево индексирование )

Операторы: | для or, & для and и ~ для not. Это должно быть сгруппированы с помощью скобок.

Чтобы удалить все строки, где столбец "Score" <50 и> 20

df = df.drop(df[(df.score < 50) & (df.score > 20)].index)

500
User

Я искал решение для этого, и я наткнулся на очевидный подход, который состоит в том, чтобы просто отфильтровать фрейм данных и присвоить исходный фрейм данных так,

df = df[df["score"] > 50]
55
Aquatically Challenged Fish

В pandas вы можете использовать str.len со своей границей и использовать логический результат для его фильтрации. 

df[df['column name'].str.len().lt(2)]
3
Wen-Ben

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

text_data = df ['name']. tolist ()

теперь примените некоторую функцию к каждому элементу списка и поместите его в серию панда:

text_length = pd.Series ([func (t) для t в text_data])

в моем случае я просто пытался получить количество токенов:

text_length = pd.Series ([len (t.split ()) для t в text_data])

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

df = df.assign (text_length = text_length .values)

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

df = df [df.text_length> 10]

Фильтр нижних/верхних частот для текста с этим выглядит следующим образом:

def pass_filter(df, label, length, pass_type):

    text_data = df[label].tolist()

    text_length = pd.Series([len(t.split()) for t in text_data])

    df = df.assign(text_length = text_length .values)

    if pass_type == 'high':
        df = df[df.text_length  >  length]

    if pass_type == 'low':
        df = df[df.text_length  <  length]

    df = df.drop(columns=['text_length'])

    return df

1
jayanti prasad

Я остановлюсь на общем решении @ User, чтобы предоставить бесплатную альтернативу drop. Это для людей, направленных здесь на основе названия вопроса (не проблема ОП) 

Скажем, вы хотите удалить все строки с отрицательными значениями. Одно решение лайнера является: -

df = df[(df > 0).all(axis=1)]

Пошаговое объяснение: -

Давайте сгенерируем случайный кадр данных нормального распределения 5x5

np.random.seed(0)
df = pd.DataFrame(np.random.randn(5,5), columns=list('ABCDE'))
      A         B         C         D         E
0  1.764052  0.400157  0.978738  2.240893  1.867558
1 -0.977278  0.950088 -0.151357 -0.103219  0.410599
2  0.144044  1.454274  0.761038  0.121675  0.443863
3  0.333674  1.494079 -0.205158  0.313068 -0.854096
4 -2.552990  0.653619  0.864436 -0.742165  2.269755

Пусть условие удаляет негативы. Логическое значение df, удовлетворяющее условию: - 

df > 0
      A     B      C      D      E
0   True  True   True   True   True
1  False  True  False  False   True
2   True  True   True   True   True
3   True  True  False   True  False
4  False  True   True  False   True

Булева серия для всех строк, удовлетворяющих условию Обратите внимание: если какой-либо элемент в строке не соответствует условию, строка помечается как ложная

(df > 0).all(axis=1)
0     True
1    False
2     True
3    False
4    False
dtype: bool

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

df[(df > 0).all(axis=1)]
      A         B         C         D         E
0  1.764052  0.400157  0.978738  2.240893  1.867558
2  0.144044  1.454274  0.761038  0.121675  0.443863

Вы можете присвоить его обратно df для фактического удаления vs фильтра, сделанного выше
df = df[(df > 0).all(axis=1)]

Это может быть легко расширено для фильтрации строк, содержащих NaN (не числовые записи): -
df = df[(~df.isnull()).all(axis=1)] 

Это также может быть упрощено для случаев, таких как: Удалить все строки, где столбец E является отрицательным 

df = df[(df.E>0)]

Я хотел бы закончить некоторыми статистическими данными о том, почему решение @ User drop медленнее, чем простая фильтрация на основе столбцов: 

%timeit df_new = df[(df.E>0)]
345 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit dft.drop(dft[dft.E < 0].index, inplace=True)
890 µs ± 94.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Столбец - это, по сути, Series, т.е. массив NumPy, он может быть проиндексирован без каких-либо затрат. Для людей, интересующихся тем, как основополагающая организация памяти влияет на скорость выполнения, это отличная ссылка Ссылка на ускорение работы панд

1
Zakir