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

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

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

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

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

KeyError: u'no item named False'

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

4 ответа

Решение

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

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

Чтобы напрямую ответить на заголовок этого вопроса (который, как я понимаю, не обязательно является проблемой ОП, но может помочь другим пользователям, сталкивающимся с этим вопросом), один из способов сделать это - использовать метод 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)

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

df = df[df["score"] > 50]

Я буду расширять общее решение @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, чтобы фактически удалить против фильтрации, выполненной выше
df = df[(df > 0).all(axis=1)]

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

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

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

Я хотел бы закончить с некоторой профилирующей статистикой о том, почему @User's 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 массив, он может быть проиндексирован без каких-либо затрат. Для людей, интересующихся тем, как основная организация памяти влияет на скорость выполнения, вот отличная ссылка на ускорение работы Pandas:

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

df[df['column name'].str.len().lt(2)]

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

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

Другие вопросы по тегам