Замена пустых значений (пробел) на NaN в пандах

152

Я хочу найти все значения в кадре данных Pandas, которые содержат пробелы (любое произвольное количество) и заменить эти значения на NaN.

Есть идеи, как это можно улучшить?

В основном я хочу включить это:

                   A    B    C
2000-01-01 -0.532681  foo    0
2000-01-02  1.490752  bar    1
2000-01-03 -1.387326  foo    2
2000-01-04  0.814772  baz     
2000-01-05 -0.222552         4
2000-01-06 -1.176781  qux     

В это:

                   A     B     C
2000-01-01 -0.532681   foo     0
2000-01-02  1.490752   bar     1
2000-01-03 -1.387326   foo     2
2000-01-04  0.814772   baz   NaN
2000-01-05 -0.222552   NaN     4
2000-01-06 -1.176781   qux   NaN

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

for i in df.columns:
    df[i][df[i].apply(lambda i: True if re.search('^\s*$', str(i)) else False)]=None

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

if df[i].dtype == np.dtype('object')

Но это не так много улучшения

И наконец, этот код устанавливает целевые строки в None, что работает с функциями Pandas fillna(), но было бы неплохо для полноты, если бы я мог вставить NaNнепосредственно вместо None.

Крис Кларк
источник
2
То, что вы действительно хотите, это возможность использовать replaceс регулярным выражением ... (возможно, это следует запрашивать как функцию).
Энди Хейден
3
Я сделал проблему с github для этой функции: github.com/pydata/pandas/issues/2285 . Был бы благодарен за PR! :)
Чанг Ше
Для тех, кто хочет превратить ровно один пустой символ в пропущенное, см. Это простое решение ниже
Тед Петру

Ответы:

200

Я думаю, что df.replace()делает работу, так как панды 0,13 :

df = pd.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'foo', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', '  '],         
], columns='A B C'.split(), index=pd.date_range('2000-01-01','2000-01-06'))

# replace field that's entirely space (or empty) with NaN
print(df.replace(r'^\s*$', np.nan, regex=True))

Производит:

                   A    B   C
2000-01-01 -0.532681  foo   0
2000-01-02  1.490752  bar   1
2000-01-03 -1.387326  foo   2
2000-01-04  0.814772  baz NaN
2000-01-05 -0.222552  NaN   4
2000-01-06 -1.176781  qux NaN

Как указал Темак , используйте, df.replace(r'^\s+$', np.nan, regex=True)если ваши действительные данные содержат пробелы.

patricksurry
источник
1
регулярное выражение является логическим флагом. Может быть, вы имеете в виду, pd.Series(["1", "#", "9", " .", None]).replace(r"( +\.)|#", "X", regex=True).valuesчто дает['1', 'X', '9', 'X', None]
patricksurry
2
2 года спустя я изменил принятый ответ на этот вопрос, теперь, когда Панды его поддерживают. Спасибо!
Крис Кларк
35
ПРИМЕЧАНИЕ : если вы не хотите, чтобы элемент, содержащий пробел в середине, был заменен на использование NaNdf.replace(r'^\s+$', np.nan, regex=True)
Temak
7
Я попытался использовать это, но обнаружил, что r '^ \ s * $' должно быть используемым выражением. без ^ и $ он будет соответствовать любой строке с двумя последовательными пробелами. Также изменилось + на *, чтобы включить пустую строку "" в список вещей для преобразования в NaN
Мастер Йогурт
1
Я пробую ваше решение в моем коде, но оно не имеет никакого эффекта. Я пытаюсь "энергия [" Энергоснабжение "]. Replace (to_replace =" ... ", value = np.NaN)". Желая изменить строку «...» на значения NaN, но она ничего не делает и возвращает тот же кадр данных.
Арчан Джоши
50

Если вы хотите заменить пустую строку и записи только пробелами, правильный ответ :!

df = df.replace(r'^\s*$', np.nan, regex=True)

Принятый ответ

df.replace(r'\s+', np.nan, regex=True)

Не заменяет пустую строку !, вы можете попробовать себя с немного обновленным примером:

df = pd.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'fo o', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', ''],         
], columns='A B C'.split(), index=pd.date_range('2000-01-01','2000-01-06'))

Также обратите внимание, что «fo o» не заменяется на Nan, хотя он содержит пробел. Далее обратите внимание, что это просто:

df.replace(r'', np.NaN)

Также не работает - попробуйте.

Филипп Шварц
источник
33

Как насчет:

d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and x.isspace() else x)

applymapФункция применяет функцию к каждой клетке dataframe.

BrenBarn
источник
Какое приятное улучшение! Я должен был подумать об этом в ретроспективе, но по какой-то причине зациклился на булевых заменах. Один вопрос - есть ли преимущество в выполнении проверки basestring по сравнению с просто str (x) .isspace ()?
Крис Кларк
1
@ChrisClark: Либо все хорошо, хотя я думаю, что это isinstanceбудет немного быстрее.
BrenBarn
13
Ссылка на "basestring" в приведенном выше коде не будет работать в Python 3 .... в этом случае попробуйте вместо этого использовать "str".
Спайк Уильямс
4
Обратите внимание, что это решение не заменяет пустые строки ''. Чтобы рассмотреть также пустые строки, используйте:d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and (not x or x.isspace()) else x)
tuomastik
18

Я сделал это:

df = df.apply(lambda x: x.str.strip()).replace('', np.nan)

или

df = df.apply(lambda x: x.str.strip() if isinstance(x, str) else x).replace('', np.nan)

Вы можете удалить все строки, а затем заменить пустые строки на np.nan.

Сяоронг Ляо
источник
лямбда x: x.str.strip () должна быть лямбда x: x.strip ()? незначительное предложение: добавьте .astype (str) впереди, это решит другие проблемы с данными для меня. Это работает для меня: df = df.apply ['column']. Astype (str) .apply (lambda x: x.strip ()). Replace ('', np.nan)
Wouter
Вторая строка кода обрабатывает столбцы типа int / float и string. Ницца. Tks!
Кейт
6

Самое простое из всех решений:

df = df.replace(r'^\s+$', np.nan, regex=True)
Джил Баджо
источник
5

Если вы экспортируете данные из CSV-файла, это может быть так просто:

df = pd.read_csv(file_csv, na_values=' ')

Это создаст фрейм данных, а также заменит пустые значения как Na

Ибрагим Рупавала
источник
2
Другая опция… using skipinitialspace=Trueтакже удаляет все пробелы после разделителя, что может привести к тому, что пустые строки будут считываться как пустые строки nan. Однако, если вы хотите сохранить начальные пробелы по какой-либо причине, этот вариант не является хорошим выбором.
Раджшекар Редди
1
@RajshekarReddy Можете ли вы, пожалуйста, где-то это ответить, это было великолепно!
User2321
2

Для очень быстрого и простого решения, где вы проверяете равенство по одному значению, вы можете использовать maskметод.

df.mask(df == ' ')
Тед Петру
источник
1

Все они близки к правильному ответу, но я бы не сказал, что это решит проблему, оставаясь наиболее читабельным для других, читающих ваш код. Я бы сказал, что ответ - это комбинация ответа BrenBarn и комментария tuomasttik под этим ответом . В ответе BrenBarn используется isspaceвстроенная функция , но она не поддерживает удаление пустых строк в соответствии с запросом OP, и я склонен приписывать это стандартному сценарию замены строк на ноль.

Я переписал это с .apply, так что вы можете позвонить на pd.Seriesили pd.DataFrame.


Python 3:

Чтобы заменить пустые строки или строки полностью пробелов:

df = df.apply(lambda x: np.nan if isinstance(x, str) and (x.isspace() or not x) else x)

Чтобы заменить строки полностью пробелов:

df = df.apply(lambda x: np.nan if isinstance(x, str) and x.isspace() else x)

Чтобы использовать это в Python 2, вам нужно заменить strна basestring.

Python 2:

Чтобы заменить пустые строки или строки полностью пробелов:

df = df.apply(lambda x: np.nan if isinstance(x, basestring) and (x.isspace() or not x) else x)

Чтобы заменить строки полностью пробелов:

df = df.apply(lambda x: np.nan if isinstance(x, basestring) and x.isspace() else x)
spen.smith
источник
1

Это сработало для меня. Когда я импортирую свой CSV-файл, я добавил na_values ​​= ''. Пробелы не включены в значения NaN по умолчанию.

df = pd.read_csv (filepath, na_values ​​= '')

sambrowne
источник
0

Вы также можете использовать фильтр, чтобы сделать это.

df = PD.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'foo', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', '  '])
    df[df=='']='nan'
    df=df.astype(float)
ERIC
источник
Каждая строка этого кода (не включая данные) является ошибочной.
Юлий
0
print(df.isnull().sum()) # check numbers of null value in each column

modifiedDf=df.fillna("NaN") # Replace empty/null values with "NaN"

# modifiedDf = fd.dropna() # Remove rows with empty values

print(modifiedDf.isnull().sum()) # check numbers of null value in each column
Jayantha
источник
0

Это не элегантное решение, но, похоже, работает сохранение в XLSX и его импорт обратно. Другие решения на этой странице не работали для меня, не знаю почему.

data.to_excel(filepath, index=False)
data = pd.read_excel(filepath)
Дэвид Конг
источник