Удалить строки из DataFrame pandas на основе условного выражения с использованием len (string), что дает KeyError

У меня есть DataFrame pandas, и я хочу удалить из него строки, длина строки в определенном столбце которых больше 2.

Я ожидаю, что смогу сделать это (за этот ответ):

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

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

KeyError: u'no item named False'

Что я делаю неправильно?

(Примечание: я знаю, что могу использовать df.dropna() чтобы избавиться от строк, содержащих какой-либо NaN, но я не видел, как удалить строки на основе условного выражения.)

Ответ 1

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

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

Ответ 2

Для прямого ответа на этот вопрос оригинальное название "Как удалить строки из DataFrame панды на основе условного выражения" (что, как я понимаю, не обязательно является проблемой OP, но может помочь другим пользователям, сталкивающимся с этим вопросом), один из способов сделать это - использовать метод отбрасывания:

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)

Ответ 3

Вы можете назначить DataFrame для своей отфильтрованной версии:

df = df[df.score > 50]

Это быстрее, чем drop:

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test = test[test.x < 0]
# 54.5 ms ± 2.02 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test.drop(test[test.x > 0].index, inplace=True)
# 201 ms ± 17.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test = test.drop(test[test.x > 0].index)
# 194 ms ± 7.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Ответ 4

Я буду расширять универсальное решение @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 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:

Ответ 5

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

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

Ответ 6

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

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

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

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

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

text_length = pd.Series([len(t.split()) for t in 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

Ответ 7

Чтобы удалить строку или n строк из кадра данных Pandas, вы можете использовать следующую команду

DataFrame.drop(labels=None, axis=0, index=None, columns=None, level=None, inplace=False, errors='raise')

Ссылка: https://www.toogit.com/tlc/article/how-to-delete-rows-from-pandas-dataframe