Удаление строк, содержащих пустые ячейки, из фрейма данных pandas

87

У меня есть файл, pd.DataFrameкоторый был создан путем анализа некоторых таблиц Excel. Столбец, в котором есть пустые ячейки. Например, ниже приведены выходные данные для частоты этого столбца, 32320 записей имеют пропущенные значения для Tenant .

>>> value_counts(Tenant, normalize=False)
                              32320
    Thunderhead                8170
    Big Data Others            5700
    Cloud Cruiser              5700
    Partnerpedia               5700
    Comcast                    5700
    SDP                        5700
    Agora                      5700
    dtype: int64

Я пытаюсь удалить строки, в которых отсутствует Tenant, однако .isnull()опция не распознает отсутствующие значения.

>>> df['Tenant'].isnull().sum()
    0

Столбец имеет тип данных «Объект». Что в этом случае происходит? Как я могу удалить записи, в которых отсутствует арендатор ?

Амрита Савант
источник

Ответы:

174

Pandas распознает значение как null, если это np.nanобъект, который будет печатать как NaNв DataFrame. Ваши отсутствующие значения, вероятно, являются пустыми строками, которые Pandas не распознает как null. Чтобы исправить это, вы можете преобразовать пустые строки (или все, что находится в ваших пустых ячейках) в np.nanобъекты, использующие replace(), а затем вызвать dropna()свой DataFrame для удаления строк с нулевыми арендаторами.

Чтобы продемонстрировать, мы создаем DataFrame с некоторыми случайными значениями и некоторыми пустыми строками в Tenantsстолбце:

>>> import pandas as pd
>>> import numpy as np
>>> 
>>> df = pd.DataFrame(np.random.randn(10, 2), columns=list('AB'))
>>> df['Tenant'] = np.random.choice(['Babar', 'Rataxes', ''], 10)
>>> print df

          A         B   Tenant
0 -0.588412 -1.179306    Babar
1 -0.008562  0.725239         
2  0.282146  0.421721  Rataxes
3  0.627611 -0.661126    Babar
4  0.805304 -0.834214         
5 -0.514568  1.890647    Babar
6 -1.188436  0.294792  Rataxes
7  1.471766 -0.267807    Babar
8 -1.730745  1.358165  Rataxes
9  0.066946  0.375640         

Теперь мы заменяем любые пустые строки в Tenantsстолбце np.nanобъектами, например:

>>> df['Tenant'].replace('', np.nan, inplace=True)
>>> print df

          A         B   Tenant
0 -0.588412 -1.179306    Babar
1 -0.008562  0.725239      NaN
2  0.282146  0.421721  Rataxes
3  0.627611 -0.661126    Babar
4  0.805304 -0.834214      NaN
5 -0.514568  1.890647    Babar
6 -1.188436  0.294792  Rataxes
7  1.471766 -0.267807    Babar
8 -1.730745  1.358165  Rataxes
9  0.066946  0.375640      NaN

Теперь мы можем отбросить нулевые значения:

>>> df.dropna(subset=['Tenant'], inplace=True)
>>> print df

          A         B   Tenant
0 -0.588412 -1.179306    Babar
2  0.282146  0.421721  Rataxes
3  0.627611 -0.661126    Babar
5 -0.514568  1.890647    Babar
6 -1.188436  0.294792  Rataxes
7  1.471766 -0.267807    Babar
8 -1.730745  1.358165  Rataxes
McMath
источник
Большое спасибо, я попробую и вернусь!
Амрита Савант
2
@mcmath, немного любопытно. Почему вы импортируете numpy и используете, np.nanкогда это возможно pd.np.nan?
propjk007
3
@ propjk007, как и со многими вещами в жизни, есть много способов сделать много вещей
Андрей
Из моих тестов кажется, что выполнение df[df['Tenant'].astype(bool)](при условии отсутствия пробельных символов - только пустая строка) быстрее, чемdf.replace('', np.nan).dropna(subset=['Tenant'])
cs95
43

Pythonic + Pandorable: df[df['col'].astype(bool)]

Пустые строки являются ложными, что означает, что вы можете фильтровать значения типа bool следующим образом:

df = pd.DataFrame({
    'A': range(5),
    'B': ['foo', '', 'bar', '', 'xyz']
})
df
   A    B
0  0  foo
1  1     
2  2  bar
3  3     
4  4  xyz
df['B'].astype(bool)                                                                                                                      
0     True
1    False
2     True
3    False
4     True
Name: B, dtype: bool

df[df['B'].astype(bool)]                                                                                                                  
   A    B
0  0  foo
2  2  bar
4  4  xyz

Если ваша цель - удалить не только пустые строки, но и строки, содержащие только пробелы, используйте str.stripзаранее:

df[df['B'].str.strip().astype(bool)]
   A    B
0  0  foo
2  2  bar
4  4  xyz

Быстрее, чем вы думаете

.astypeявляется векторизованной операцией, это быстрее, чем все варианты, представленные до сих пор. По крайней мере, из моих тестов. YMMV.

Вот сравнение времени, я добавил несколько других методов, которые я мог придумать.

введите описание изображения здесь

Код тестирования, для справки:

import pandas as pd
import perfplot

df1 = pd.DataFrame({
    'A': range(5),
    'B': ['foo', '', 'bar', '', 'xyz']
})

perfplot.show(
    setup=lambda n: pd.concat([df1] * n, ignore_index=True),
    kernels=[
        lambda df: df[df['B'].astype(bool)],
        lambda df: df[df['B'] != ''],
        lambda df: df[df['B'].replace('', np.nan).notna()],  # optimized 1-col
        lambda df: df.replace({'B': {'': np.nan}}).dropna(subset=['B']),  
    ],
    labels=['astype', "!= ''", "replace + notna", "replace + dropna", ],
    n_range=[2**k for k in range(1, 15)],
    xlabel='N',
    logx=True,
    logy=True,
    equality_check=pd.DataFrame.equals)
cs95
источник
33

value_counts по умолчанию опускает NaN, поэтому вы, скорее всего, имеете дело с "".

Так что вы можете просто отфильтровать их, как

filter = df["Tenant"] != ""
dfNew = df[filter]
Боб Хаффнер
источник
1
Решение @Bobs у меня не сработало. df.dropna (subset = ['tenant'], inplace = True) работает.
Амрита Савант,
1
Прости за это. Я думал, ты имеешь дело с "" с. Вы должны опубликовать свое решение в качестве ответа
Боб Хаффнер,
8

Есть ситуация, когда в ячейке есть белое пространство, вы его не видите, используйте

df['col'].replace('  ', np.nan, inplace=True)

чтобы заменить пробел на NaN, затем

df= df.dropna(subset=['col'])
Учиться
источник
4

Вы можете использовать этот вариант:

import pandas as pd
vals = {
    'name' : ['n1', 'n2', 'n3', 'n4', 'n5', 'n6', 'n7'],
    'gender' : ['m', 'f', 'f', 'f',  'f', 'c', 'c'],
    'age' : [39, 12, 27, 13, 36, 29, 10],
    'education' : ['ma', None, 'school', None, 'ba', None, None]
}
df_vals = pd.DataFrame(vals) #converting dict to dataframe

Это выведет (** - выделение только желаемых строк):

   age education gender name
0   39        ma      m   n1 **
1   12      None      f   n2    
2   27    school      f   n3 **
3   13      None      f   n4
4   36        ba      f   n5 **
5   29      None      c   n6
6   10      None      c   n7

Чтобы отказаться от всего, что не имеет значения «образование», используйте приведенный ниже код:

df_vals = df_vals[~df_vals['education'].isnull()] 

('~' означает НЕ)

Результат:

   age education gender name
0   39        ma      m   n1
2   27    school      f   n3
4   36        ba      f   n5
Амир Ф
источник