Видалити рядки з панд DataFrame на основі умовного виразу, що включає len (рядок), що дає KeyError


303

У мене є пандес DataFrame, і я хочу видалити з нього рядки, де довжина рядка в певному стовпчику перевищує 2.

Я очікую, що зможу це зробити (за цією відповіддю ):

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

але я просто отримую помилку:

KeyError: u'no item named False'

Що я роблю неправильно?

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

Відповіді:


168

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

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

3
Я придумав спосіб із розумінням списку: df[[(len(x) < 2) for x in df['column name']]]але ваш набагато приємніший. Спасибі за вашу допомогу!
sjs

13
Якщо комусь потрібен складніший компарамент, завжди можна використовувати лямбда. df[df['column name'].map(lambda x: str(x)!=".")]
4lberto

1
Чомусь жоден з інших варіантів для мене не працював, за винятком того, який опублікував @ 4lberto. Я на pandas 0.23.4і python 3.6
goelakash

1
Я додав би .copy()в кінці, якщо ви хочете пізніше відредагувати цей фрейм даних (наприклад, при призначенні нових стовпців буде підвищено "Попередження намагається встановити на копії фрагмента з DataFrame"
PlasmaBinturong

806

Для прямої відповіді на оригінальну назву цього питання "Як видалити рядки з панд DataFrame на основі умовного виразу" (що я розумію, це не обов'язково проблема ОП, але може допомогти іншим користувачам, які стикаються з цим питанням), один із способів зробити це - використовувати краплі метод:

df = df.drop(some labels)

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

Приклад

Щоб видалити всі рядки, де стовпець "оцінка" <50:

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

На місці версія (як зазначено в коментарях)

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

Кілька умов

(див. булеву індексацію )

Оператори: |для or, &для andі ~для not. Вони повинні бути згруповані за допомогою дужок.

Щоб видалити всі рядки, де стовпець "оцінка" <50 та> 20

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


32
Я просто хочу зауважити, що функція drop підтримує заміну заміни. Тобто,. ваше рішення те саме, що df.drop (df [df.score <50] .index, inplace = True). Тим не менш, не знав трюку "індексу". Мені дуже допомогли
Quickbeam2k1

9
Просто хочу зазначити, що перед тим, як скористатися цим трюком індексу, ви повинні бути впевнені, що ваші значення індексу унікальні (або виклику reset_index()). Я виявив це важким шляхом, коли шлях до багатьох рядків випав із мого фрейму даних.
Джей

3
як я скидаю всі рядки, де тип стовпця str? Я хочу зберегти лише типи стовпців списку. Я спробував , test = df.drop(df[df['col1'].dtype == str].index)але я отримую помилку KeyError: False я також спробував df.drop(df[df.col1.dtype == str].index)і , df.drop(df[type(df.cleaned_norm_email) == str].index)але нічого не схоже на роботу? Хтось може порадити. Дякую! @User
PyRsquared

1
Це давнє запитання, але ... @ риба, що викликає водний рух, набагато швидша за цю. Зауважте, що ви обчислюєте df[(df.score < 50) & (df.score > 20)]як частину своєї відповіді. Якби ви це змінили, df = df[(df.score >= 50) | (df.score <= 20)]ви отримаєте відповідь набагато швидше.
Робі Нубі

1
@RoobieNuby - вони не однакові умови.
Nguai al

106

Ви можете призначити 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)

Як перевірити використання чи умова для кількох стовпців?
Піюш С. Ванаре


9

Я розширюю загальне рішення @ Користувача, щоб запропонувати 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 s (не числові записи): -
df = df[(~df.isnull()).all(axis=1)]

Це також може бути спрощено для таких випадків, як: Видалити всі рядки, де стовпець E від'ємний

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

Я хотів би закінчити кілька статистичних даних про те, чому рішення @ користувача 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його можна індексувати без будь-яких витрат. Для людей , зацікавлених в тому , як лежить в основі організація пам'ять грає в швидкість виконання тут є відмінною Посилання на Форсування панди :


6

У пандах ви можете виконати str.lenсвою межу та за допомогою булевого результату відфільтрувати її.

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

3

Якщо ви хочете опустити рядки кадру даних на підставі якоїсь складної умови на значення стовпця, тоді написання цього способу може бути складним. У мене є таке простіше рішення, яке завжди працює. Припустимо, що ви хочете опустити стовпчик із "заголовком", тому спочатку поставте цей стовпець у список.

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.