Konversi kolom Pandas yang berisi NaN ke dtype `int`

175

Saya membaca data dari file .csv ke bingkai data Pandas seperti di bawah ini. Untuk salah satu kolom, yaitu id, saya ingin menentukan jenis kolom sebagai int. Masalahnya adalah idseri memiliki nilai yang hilang / kosong.

Ketika saya mencoba untuk melemparkan idkolom ke integer saat membaca .csv, saya mendapatkan:

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

Atau, saya mencoba mengonversi jenis kolom setelah membaca seperti di bawah ini, tetapi kali ini saya mendapatkan:

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

Bagaimana saya bisa mengatasi ini?

Zhubarb
sumber
3
Saya pikir nilai integer tidak dapat dikonversi atau disimpan dalam seri / dataframe jika ada nilai yang hilang / NaN. Ini saya pikir berkaitan dengan kompatibilitas numpy (saya kira di sini), jika Anda ingin kompatibilitas nilai yang hilang maka saya akan menyimpan nilai sebagai mengapung
EdChum
1
lihat di sini: pandas.pydata.org/pandas-docs/dev/… ; Anda harus memiliki float dtype ketika Anda memiliki nilai yang hilang (atau secara teknis objek dtype tetapi itu tidak efisien); apa tujuan Anda menggunakan tipe int?
Jeff
6
Saya percaya ini adalah masalah NumPy, tidak khusus untuk Panda. Ini memalukan karena ada begitu banyak kasus ketika memiliki tipe int yang memungkinkan untuk kemungkinan nilai nol jauh lebih efisien daripada kolom besar mengapung.
ely
1
Saya punya masalah dengan ini juga. Saya memiliki beberapa dataframe yang ingin saya gabungkan berdasarkan representasi string dari beberapa kolom "integer". Namun, ketika salah satu kolom integer memiliki np.nan, casting string menghasilkan ".0", yang melempar gabungan. Hanya membuat hal-hal sedikit lebih rumit, alangkah baiknya jika ada pekerjaan sederhana di sekitar.
dermen
1
@Rhubarb, Dukungan Integer Nullable Opsional sekarang secara resmi ditambahkan di panda 0.24.0 - akhirnya :) - silakan temukan jawaban yang diperbarui di bawah. panda 0.24.x catatan rilis
mork

Jawaban:

169

Kurangnya rep NaN dalam kolom integer adalah panda "gotcha" .

Solusi yang biasa adalah dengan hanya menggunakan pelampung.

Andy Hayden
sumber
13
Apakah ada solusi lain selain memperlakukannya seperti pelampung?
NumenorForLife
3
@ jsc123 Anda dapat menggunakan objek dtype. Ini datang dengan peringatan kesehatan yang kecil tetapi sebagian besar berfungsi dengan baik.
Andy Hayden
1
Bisakah Anda memberikan contoh cara menggunakan objek dtype? Saya telah melihat-lihat panda docs dan googling, dan saya sudah membaca ini metode yang direkomendasikan. Tapi, saya belum menemukan contoh cara menggunakan objek dtype.
MikeyE
28
Di v0.24, sekarang Anda dapat melakukan df = df.astype(pd.Int32Dtype())(untuk mengonversi seluruh dataFrame, atau) df['col'] = df['col'].astype(pd.Int32Dtype()). Jenis integer nullable yang diterima lainnya adalah pd.Int16Dtypedan pd.Int64Dtype. Pilih racunmu.
cs95
1
Ini adalah nilai NaN tetapi pengecekan isnan tidak bekerja sama sekali :(
Winston
116

Dalam versi 0.24. + Panda telah memperoleh kemampuan untuk menahan dtypes integer dengan nilai yang hilang.

Tipe Data Integer Nullable .

Panda dapat mewakili data integer dengan nilai yang kemungkinan hilang arrays.IntegerArray. Ini adalah jenis ekstensi yang diterapkan dalam panda. Ini bukan tipe default untuk bilangan bulat, dan tidak akan disimpulkan; Anda harus secara eksplisit memasukkan dtype ke array()atau Series:

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

0      1
1      2
2    NaN
dtype: Int64

Untuk kolom konversi ke bilangan bulat nullable gunakan:

df['myCol'] = df['myCol'].astype('Int64')
jezrael
sumber
4
Saya suka jawaban ini.
cs95
7
Perhatikan bahwa dtype harus "Int64"dan tidak "int64"(pertama 'saya' harus dikapitalisasi)
Viacheslav Z
2
df.myCol = df.myCol.astype('Int64')ataudf['myCol'] = df['myCol'].astype('Int64')
LoMaPh
43

Kasing penggunaan saya adalah munging data sebelum memuat ke tabel DB:

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)

Hapus NaN, konversikan ke int, konversikan ke str, lalu masukkan kembali NAN.

Itu tidak cantik tapi itu menyelesaikan pekerjaan!

hibernado
sumber
1
Saya telah mencabut rambut saya mencoba memuat nomor seri di mana ada yang nol dan sisanya mengapung, ini menyelamatkan saya.
Chris Decker
1
OP menginginkan kolom bilangan bulat. Mengubahnya menjadi string tidak memenuhi persyaratan.
Rishab Gupta
1
Hanya berfungsi jika col belum memiliki -1. Kalau tidak, itu akan mengacaukan data
Sharvari Gc
lalu bagaimana cara kembali ke int .. ??
abdoulsn
5

Sekarang dimungkinkan untuk membuat kolom panda yang berisi NaN sebagai dtype int, karena sekarang secara resmi ditambahkan pada panda 0.24.0

panda 0.24.x catatan rilis Quote: " Pandas telah memperoleh kemampuan untuk memegang dtypes integer dengan nilai yang hilang

mork
sumber
4

Jika Anda benar-benar ingin menggabungkan bilangan bulat dan NaN dalam kolom, Anda dapat menggunakan tipe data 'objek':

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

Ini akan menggantikan NaN dengan integer (tidak peduli yang mana), konversikan ke int, konversikan ke objek dan akhirnya masukkan kembali NaNs.

jenglund
sumber
3

Jika Anda dapat mengubah data yang disimpan, gunakan nilai sentinel untuk hilang id. Kasus penggunaan umum, disimpulkan dengan nama kolom, idadalah integer, lebih besar dari nol, Anda dapat menggunakan 0sebagai nilai sentinel sehingga Anda dapat menulis

if row['id']:
   regular_process(row)
else:
   special_process(row)
gboffi
sumber
3

Anda bisa menggunakan .dropna()jika OK untuk menjatuhkan baris dengan nilai NaN.

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

Atau, gunakan .fillna()dan .astype()untuk menggantikan NaN dengan nilai-nilai dan mengubahnya menjadi int.

Saya mengalami masalah ini saat memproses file CSV dengan integer besar, sementara beberapa di antaranya hilang (NaN). Menggunakan float sebagai tipenya bukan pilihan, karena saya mungkin kehilangan presisi.

Solusi saya adalah menggunakan str sebagai tipe perantara . Kemudian Anda dapat mengonversi string menjadi int sesuka Anda dalam kode. Saya mengganti NaN dengan 0, tetapi Anda dapat memilih nilai apa pun.

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

Sebagai ilustrasi, berikut adalah contoh bagaimana pelampung bisa kehilangan presisi:

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

Dan hasilnya adalah:

1.2345678901234567e+19 12345678901234567168 12345678901234567890
elomage
sumber
2

Sebagian besar solusi di sini memberi tahu Anda cara menggunakan integer placeholder untuk mewakili nol. Pendekatan itu tidak membantu jika Anda ragu integer tidak akan muncul di data sumber Anda. Metode saya dengan format Will mengapung tanpa nilai desimalnya dan mengonversi nol menjadi Tidak ada. Hasilnya adalah tipe data objek yang akan terlihat seperti bidang bilangan bulat dengan nilai nol saat dimuat ke CSV.

keep_df[col] = keep_df[col].apply(lambda x: None if pandas.isnull(x) else '{0:.0f}'.format(pandas.to_numeric(x)))
Corbin
sumber
1

Saya mengalami masalah ini bekerja dengan pyspark. Karena ini adalah frontend python untuk kode yang berjalan pada jvm, ini memerlukan keamanan jenis dan menggunakan float bukan int bukan pilihan. Saya mengatasi masalah ini dengan membungkus panda pd.read_csvdalam fungsi yang akan mengisi kolom yang ditentukan pengguna dengan nilai isian yang ditentukan pengguna sebelum melemparkannya ke jenis yang diperlukan. Inilah yang akhirnya saya gunakan:

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
sumber
1
import pandas as pd

df= pd.read_csv("data.csv")
df['id'] = pd.to_numeric(df['id'])
Monaheng Ramochele
sumber
4
Apakah ada alasan Anda lebih menyukai formulasi ini daripada yang diusulkan dalam jawaban yang diterima? Jika demikian, akan berguna untuk mengedit jawaban Anda untuk memberikan penjelasan itu — dan terutama karena ada sepuluh jawaban tambahan yang bersaing untuk mendapatkan perhatian.
Jeremy Caney
Meskipun kode ini dapat menyelesaikan masalah OP, yang terbaik adalah menyertakan penjelasan tentang bagaimana / mengapa kode Anda mengatasinya. Dengan cara ini, pengunjung masa depan dapat belajar dari pos Anda, dan menerapkannya pada kode mereka sendiri. SO bukan layanan pengkodean, tetapi sumber daya untuk pengetahuan. Juga, kualitas tinggi, jawaban lengkap lebih cenderung terunggulkan. Fitur-fitur ini, bersama dengan persyaratan bahwa semua posting mandiri, adalah beberapa kekuatan SO sebagai platform membedakannya dari forum. Anda dapat editmenambahkan info tambahan & / atau untuk melengkapi penjelasan Anda dengan dokumentasi sumber.
SherylHohman
0

Pertama-tama hapus baris yang mengandung NaN. Kemudian lakukan konversi Integer pada baris yang tersisa. Terakhir masukkan baris yang dihapus lagi. Semoga ini berhasil

kamran kausar
sumber
-1

Dengan asumsi DateColumn Anda diformat 3312018.0 harus dikonversi ke 03/31/2018 sebagai string. Dan, beberapa catatan hilang atau 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'))
Justin Malinchak
sumber