Как удалить строки из 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