Заменить недопустимые значения на None в Pandas DataFrame

80

Есть ли способ заменить значения Noneв Pandas в Python?

Вы можете использовать df.replace('pre', 'post')и можете заменить одно значение другим, но этого нельзя сделать, если вы хотите заменить на Noneзначение, которое при попытке получить странный результат.

Итак, вот пример:

df = DataFrame(['-',3,2,5,1,-5,-1,'-',9])
df.replace('-', 0)

который возвращает успешный результат.

Но,

df.replace('-', None)

который возвращает следующий результат:

0
0   - // this isn't replaced
1   3
2   2
3   5
4   1
5  -5
6  -1
7  -1 // this is changed to `-1`...
8   9

Почему возвращается такой странный результат?

Поскольку я хочу залить этот фрейм данных в базу данных MySQL, я не могу поместить NaNзначения ни в один элемент в моем фрейме данных, а вместо этого хочу поместить None. Конечно, вы можете сначала изменить , '-'чтобы NaNзатем конвертировать NaNв None, но я хочу знать , почему dataframe действует таким ужасным способом.

Протестировано на pandas 0.12.0 dev на Python 2.7 и OS X 10.8. Python - это предустановленная версия для OS X, и для вашей информации я установил pandas с помощью скрипта SciPy Superpack.

Blaszard
источник
Разве write_frameне разбирает NaNs на nones?
Энди Хейден
Ага. Вы столкнулись с InternalError: (1054, u"Unknown column 'nan' in 'field list'")ошибкой. Я не знаю ни о каких решениях, кроме преобразования NaNв метод Noneперед выполнением write_frame.
Blaszard
Какую версию панд вы используете?
Энди Хейден
Scipy super pack выдает разработчика? Хорошо, я определенно думаю, что вам следует поднять это как проблему на github , это не должно быть слишком сложно исправить.
Энди Хайден
Если вы читаете эти данные из CSV / Excel , вы можете прочитать эти значения как NaN с помощью na_valuesаргумента. Больше информации в этом ответе.
cs95

Ответы:

111

На самом деле в более поздних версиях pandas это даст TypeError:

df.replace('-', None)
TypeError: If "to_replace" and "value" are both None then regex must be a mapping

Вы можете сделать это, передав список или словарь:

In [11]: df.replace('-', df.replace(['-'], [None]) # or .replace('-', {0: None})
Out[11]:
      0
0  None
1     3
2     2
3     5
4     1
5    -5
6    -1
7  None
8     9

Но я рекомендую использовать NaN, а не None:

In [12]: df.replace('-', np.nan)
Out[12]:
     0
0  NaN
1    3
2    2
3    5
4    1
5   -5
6   -1
7  NaN
8    9
Энди Хайден
источник
15
Или просто список, например df.replace(['-'], [None]), или df.replace({'-': None}), как мне кажется. Использование Noneв качестве часового также исключает использование его в качестве значения ..
DSM
@ user2360798 replace - это на самом деле очень многофункциональная (сложная для чтения) функция, хотя (dev) docstring действительно хороша.
Энди Хейден
4
Я не знаю, очевидно ли это, но мне пришлось присвоить dfсебе как:df = df.replace({'?': np.nan})
luckyging3r
3
@AndyHayden df.replace('-', df.replace(['-'], [None])выглядит круто , это опечатка?
lin_bug
2
@lin_bug Хотя кажется, что он больше не работает в последних версиях pandas. df.where (df! = '-', None) работает
Энди Хайден
17

Я предпочитаю использовать решение replaceс помощью dictиз-за его простоты и элегантности:

df.replace({'-': None})

Вы также можете иметь больше замен:

df.replace({'-': None, 'None': None})

И даже для более крупных замен всегда очевидно и ясно, что заменяется чем - что, на мой взгляд, сложнее для длинных списков.

Майкл Дорнер
источник
1
Стоит отметить, что часть того, почему этот метод работает, заключается в том, что использование dictтипа in to_replaceприводит к тому, что methodпараметр не оценивается, и, следовательно, по method='pad'умолчанию не имеет вредных последствий.
bsplosion
15

whereвероятно, то, что вы ищете. Так

data=data.where(data=='-', None) 

Из документов panda :

where [возвращает] объект той же формы, что и self, и соответствующие записи взяты из self, где cond имеет значение True, а в противном случае - от other).

user2966041
источник
5
На самом деле это неточно. data = data.where (data == '-', None) заменит все, что НЕ РАВНО '-' на None. Версия Pandas для where сохраняет значение первого аргумента (в данном случае data == '-') и заменяет все остальное вторым аргументом (в данном случае None). Это немного сбивает с толку, поскольку np.where более явный, поскольку он запрашивает условное выражение в первом аргументе, затем if true во втором аргументе, а затем if false в третьем аргументе.
clg4 04
8

Прежде чем продолжить эту публикацию, важно понять разницу между NaN и None . Один тип с плавающей запятой, другой тип объекта. Pandas лучше подходит для работы со скалярными типами, поскольку многие методы для этих типов можно векторизовать. Pandas пытается последовательно обрабатывать None и NaN, но NumPy не может.

Мое предложение ( и предложение Энди ) - придерживаться NaN.

Но чтобы ответить на ваш вопрос ...

pandas> = 0.18: использовать na_values=['-']аргумент сread_csv

Если вы загрузили эти данные из CSV / Excel, у меня для вас хорошие новости. Вы можете подавить это в корне во время загрузки данных вместо того, чтобы писать исправление с кодом в качестве следующего шага.

Большинство pd.read_*функций (например, read_csvи read_excel) принимают na_valuesатрибут.

file.csv

A,B
-,1
3,-
2,-
5,3
1,-2
-5,4
-1,-1
-,0
9,0

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

import pandas as pd
df = pd.read_csv('file.csv', na_values=['-'])
df

     A    B
0  NaN  1.0
1  3.0  NaN
2  2.0  NaN
3  5.0  3.0
4  1.0 -2.0
5 -5.0  4.0
6 -1.0 -1.0
7  NaN  0.0
8  9.0  0.0

И аналогично для других функций / форматов файлов.

PS: В версии 0.24 + вы можете сохранить целочисленный тип, даже если в вашем столбце есть NaN (да, поговорим о том, чтобы съесть торт и съесть его тоже). Вы можете указатьdtype='Int32'

df = pd.read_csv('file.csv', na_values=['-'], dtype='Int32')
df

     A    B
0  NaN    1
1    3  NaN
2    2  NaN
3    5    3
4    1   -2
5   -5    4
6   -1   -1
7  NaN    0
8    9    0

df.dtypes

A    Int32
B    Int32
dtype: object

Dtype - это не обычный тип int ... а скорее целочисленный тип, допускающий значение NULL . Есть и другие варианты.


Обработка числовых данных: pd.to_numericсerrors='coerce

Если вы имеете дело с числовыми данными, более быстрым решением является использование pd.to_numericс errors='coerce'аргументом, который принуждает недопустимые значения (значения , которые не могут быть Чугунные числовые) NaN.

pd.to_numeric(df['A'], errors='coerce')

0    NaN
1    3.0
2    2.0
3    5.0
4    1.0
5   -5.0
6   -1.0
7    NaN
8    9.0
Name: A, dtype: float64

Чтобы сохранить (обнуляемый) целочисленный dtype, используйте

pd.to_numeric(df['A'], errors='coerce').astype('Int32')

0    NaN
1      3
2      2
3      5
4      1
5     -5
6     -1
7    NaN
8      9
Name: A, dtype: Int32 

Чтобы привести несколько столбцов, используйте apply:

df[['A', 'B']].apply(pd.to_numeric, errors='coerce').astype('Int32')

     A    B
0  NaN    1
1    3  NaN
2    2  NaN
3    5    3
4    1   -2
5   -5    4
6   -1   -1
7  NaN    0
8    9    0

... и присвоить результат обратно после.

Более подробную информацию можно найти в этом ответе .

cs95
источник
3
df = pd.DataFrame(['-',3,2,5,1,-5,-1,'-',9])
df = df.where(df!='-', None)
Шраван КП
источник
0

Установка нулевых значений может быть выполнена с помощью np.nan:

import numpy as np
df.replace('-', np.nan)

Преимущество в том, что df.last_valid_index()признает их недействительными.

Фрик Викмейер
источник
0

Использование replace и назначение нового df:

import pandas as pd
df = pd.DataFrame(['-',3,2,5,1,-5,-1,'-',9])
dfnew = df.replace('-', 0)
print(dfnew)


(venv) D:\assets>py teste2.py
   0
0  0
1  3
2  2
3  5
4  1
5 -5
Дэниел Роча
источник
0
df.replace('-', np.nan).astype("object")

Это гарантирует, что вы сможете использовать isnull()позже в своем фрейме данных

Кенг Чан
источник
0

С версией Pandas ≥1.0.0 я бы использовал DataFrame.replaceили Series.replace:

df.replace(old_val, pd.NA, inplace=True)

Это лучше по двум причинам:

  1. Он использует pd.NAвместо Noneили np.nan.
  2. Он заменяет значение на месте, что может быть более эффективным с точки зрения памяти.
Acumenus
источник