Конвертировать столбец Pandas, содержащий NaN, в dtype `int`

176

Я читаю данные из файла .csv на фрейм данных Pandas, как показано ниже. Для одного из столбцов, а именно id, я хочу указать тип столбца как int. Проблема в том, что в idсерии отсутствуют / пустые значения.

Когда я пытаюсь привести idстолбец к целому числу при чтении .csv, я получаю:

df= pd.read_csv("data.csv", dtype={'id': int}) 
error: Integer column has NA values

В качестве альтернативы я попытался преобразовать тип столбца после прочтения, как показано ниже, но на этот раз я получаю:

df= pd.read_csv("data.csv") 
df[['id']] = df[['id']].astype(int)
error: Cannot convert NA to integer

Как я могу решить это?

Zhubarb
источник
3
Я думаю, что целочисленные значения не могут быть преобразованы или сохранены в серии / dataframe, если отсутствуют значения / NaN. Я думаю, что это связано с совместимостью NumPy (я предполагаю здесь), если вы хотите совместимость с отсутствующими значениями, то я бы
сохранял
1
см. здесь: pandas.pydata.org/pandas-docs/dev/… ; у вас должен быть тип с плавающей запятой, когда у вас отсутствуют значения (или технически объект dtype, но это неэффективно); Какова ваша цель использования типа int?
Джефф
6
Я считаю, что это проблема NumPy, не относящаяся к Pandas. Обидно, потому что есть очень много случаев, когда использование типа int, допускающего нулевые значения, гораздо эффективнее, чем большой столбец с плавающей точкой.
Ely
1
У меня тоже проблема с этим. У меня есть несколько фреймов данных, которые я хочу объединить на основе строкового представления нескольких «целочисленных» столбцов. Однако, когда у одного из этих целочисленных столбцов есть np.nan, приведение строки выдает «.0», который отбрасывает слияние. Просто все немного усложняет, было бы неплохо, если бы был простой обходной путь.
dermen
1
@Rhubarb, Дополнительная поддержка целочисленных значений Nullable теперь официально добавлена ​​на pandas 0.24.0 - наконец-то :) - пожалуйста, найдите обновленный ответ ниже. Замечания к выпуску pandas 0.24.x
19

Ответы:

169

Отсутствие числа NaN в целочисленных столбцах является пандой "гоча" .

Обычный обходной путь - просто использовать поплавки.

Энди Хейден
источник
13
Есть ли какие-то другие обходные пути, кроме как относиться к ним как к поплавкам?
NumenorForLife
3
@ jsc123 вы можете использовать объект dtype. Это сопровождается небольшим предупреждением о вреде для здоровья, но по большей части работает хорошо.
Энди Хейден
1
Можете ли вы привести пример использования объекта dtype? Я просматривал документы панд и гуглил, и я прочитал, что это рекомендуемый метод. Но я не нашел пример того, как использовать объект dtype.
MikeyE
30
В v0.24 вы теперь можете это сделать df = df.astype(pd.Int32Dtype())(для преобразования всего dataFrame или) df['col'] = df['col'].astype(pd.Int32Dtype()). Другие принятые обнуляемых целочисленные типы являются pd.Int16Dtypeи pd.Int64Dtype. Выбрать свой яд.
cs95
1
Это значение NaN, но проверка isnan вообще не работает :(
Уинстон,
120

В версии 0.24. + Pandas получила возможность хранить целочисленные dtypes с пропущенными значениями.

Обнуляемый целочисленный тип данных .

Панды могут представлять целочисленные данные с возможно отсутствующими значениями, используя arrays.IntegerArray. Это расширение типов, реализованное в пандах. Это не dtype по умолчанию для целых чисел, и не будет выведено; Вы должны явно передать dtype в array()или Series:

arr = pd.array([1, 2, np.nan], dtype=pd.Int64Dtype())
pd.Series(arr)

0      1
1      2
2    NaN
dtype: Int64

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

df['myCol'] = df['myCol'].astype('Int64')
jezrael
источник
4
Мне нравится этот ответ.
cs95
9
Обратите внимание, что dtype должен быть, "Int64"а не "int64"(первое «i» должно быть
Viacheslav Z
2
df.myCol = df.myCol.astype('Int64')илиdf['myCol'] = df['myCol'].astype('Int64')
LoMaPh
43

Мой сценарий использования - данные перед загрузкой в ​​таблицу БД:

df[col] = df[col].fillna(-1)
df[col] = df[col].astype(int)
df[col] = df[col].astype(str)
df[col] = df[col].replace('-1', np.nan)

Удалите NaN, преобразуйте в int, преобразуйте в str, а затем снова вставьте NAN.

Это не красиво, но это делает работу!

hibernado
источник
1
Я вырывал свои волосы, пытаясь загрузить серийные номера, некоторые из которых были нулевыми, а остальные поплавками, это спасло меня.
Крис Декер
1
ОП хочет столбец целых чисел. Преобразование в строку не соответствует условию.
Ришаб Гупта
1
Работает только если col еще не имеет -1. В противном случае, это будет
мешать
тогда как вернуться к int .. ??
abdoulsn
5

Теперь можно создать столбец панд, содержащий NaNs как dtype int, поскольку теперь он официально добавлен в панды 0.24.0.

Замечания к выпуску pandas 0.24.x Цитата: «У Pandas появилась возможность хранить целочисленные dtypes с пропущенными значениями

Mork
источник
4

Если вы абсолютно хотите объединить целые и NaN в столбце, вы можете использовать тип данных 'object':

df['col'] = (
    df['col'].fillna(0)
    .astype(int)
    .astype(object)
    .where(df['col'].notnull())
)

Это заменит NaN целым числом (неважно, какое), преобразует в int, преобразует в объект и, наконец, вставляет NaN.

jmenglund
источник
3

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

if row['id']:
   regular_process(row)
else:
   special_process(row)
gboffi
источник
3

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

df = df.dropna(subset=['id'])

Либо используйте .fillna()и .astype()для замены NaN значениями и преобразуйте их в int.

Я столкнулся с этой проблемой при обработке файла CSV с большими целыми числами, хотя некоторые из них отсутствовали (NaN). Использование float в качестве типа не было возможным, потому что я мог потерять точность.

Моим решением было использовать str в качестве промежуточного типа . Затем вы можете преобразовать строку в int, как вам будет угодно позже в коде. Я заменил NaN на 0, но вы можете выбрать любое значение.

df = pd.read_csv(filename, dtype={'id':str})
df["id"] = df["id"].fillna("0").astype(int)

Для иллюстрации, вот пример того, как поплавки могут потерять точность:

s = "12345678901234567890"
f = float(s)
i = int(f)
i2 = int(s)
print (f, i, i2)

И вывод:

1.2345678901234567e+19 12345678901234567168 12345678901234567890
elomage
источник
2

Большинство решений здесь говорят вам, как использовать целое число заполнителя для представления нулей. Такой подход не поможет, если вы не уверены, что целое число не будет отображаться в ваших исходных данных. Мой метод с форматирует поплавки без их десятичных значений и конвертирует нули в None. Результатом является объектный тип данных, который при загрузке в CSV будет выглядеть как целочисленное поле с нулевыми значениями.

keep_df[col] = keep_df[col].apply(lambda x: None if pandas.isnull(x) else '{0:.0f}'.format(pandas.to_numeric(x)))
Корбин
источник
1

Я столкнулся с этой проблемой, работая с pyspark. Поскольку это интерфейс Python для кода, работающего на jvm, он требует безопасности типов и использование float вместо int не вариант. Я обошел эту проблему, обернув панды pd.read_csvв функцию, которая будет заполнять пользовательские столбцы пользовательскими значениями заполнения, прежде чем привести их к требуемому типу. Вот что я в итоге использовал:

def custom_read_csv(file_path, custom_dtype = None, fill_values = None, **kwargs):
    if custom_dtype is None:
        return pd.read_csv(file_path, **kwargs)
    else:
        assert 'dtype' not in kwargs.keys()
        df = pd.read_csv(file_path, dtype = {}, **kwargs)
        for col, typ in custom_dtype.items():
            if fill_values is None or col not in fill_values.keys():
                fill_val = -1
            else:
                fill_val = fill_values[col]
            df[col] = df[col].fillna(fill_val).astype(typ)
    return df
Neuneck
источник
1
import pandas as pd

df= pd.read_csv("data.csv")
df['id'] = pd.to_numeric(df['id'])
Монахенг Рамочеле
источник
4
Есть ли причина, по которой вы предпочитаете эту формулировку предложенной в принятом ответе? Если это так, было бы полезно отредактировать ваш ответ, чтобы предоставить это объяснение, особенно потому, что есть десять дополнительных ответов, которые требуют внимания.
Джереми Кейни
Хотя этот код может решить проблему OP, лучше всего включить объяснение того, как / почему ваш код решает эту проблему. Таким образом, будущие посетители могут учиться на вашем посте и применять его к своему коду. SO - это не сервис кодирования, а ресурс для знаний. Кроме того, высокое качество, полные ответы, скорее всего, будут проголосованы. Эти функции, наряду с требованием, чтобы все сообщения были автономными, являются одними из сильных сторон SO, поскольку платформа отличает ее от форумов. Вы можете editдобавить дополнительную информацию и / или дополнить свои пояснения исходной документацией.
Шерил Хохман
0

Сначала удалите строки, которые содержат NaN. Затем выполните целочисленное преобразование оставшихся строк. Наконец, вставьте удаленные строки снова. Надеюсь, это сработает

Камран Каусар
источник
-1

Предполагая, что ваш DateColumn в формате 3312018.0 должен быть преобразован в 31.03.2008 в виде строки. И некоторые записи отсутствуют или 0.

df['DateColumn'] = df['DateColumn'].astype(int)
df['DateColumn'] = df['DateColumn'].astype(str)
df['DateColumn'] = df['DateColumn'].apply(lambda x: x.zfill(8))
df.loc[df['DateColumn'] == '00000000','DateColumn'] = '01011980'
df['DateColumn'] = pd.to_datetime(df['DateColumn'], format="%m%d%Y")
df['DateColumn'] = df['DateColumn'].apply(lambda x: x.strftime('%m/%d/%Y'))
Джастин Малинчак
источник