Przekonwertuj kolumnę Pandy zawierającą NaN na dtype „int”

175

Czytam dane z pliku .csv do ramki danych Pandas, jak poniżej. W przypadku jednej z kolumn idchcę określić typ kolumny jako int. Problem polega na tym, że idseria ma brakujące / puste wartości.

Kiedy próbuję rzutować idkolumnę na liczbę całkowitą podczas czytania pliku .csv, otrzymuję:

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

Ewentualnie próbowałem przekonwertować typ kolumny po przeczytaniu jak poniżej, ale tym razem otrzymuję:

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

Jak sobie z tym poradzić?

Zhubarb
źródło
3
Myślę, że wartości całkowite nie mogą być konwertowane ani przechowywane w serii / ramce danych, jeśli brakuje wartości / NaN. Myślę, że ma to związek z kompatybilnością numpy (zgaduję tutaj), jeśli chcesz kompatybilności z brakującymi wartościami, zapamiętałbym wartości jako
zmiennoprzecinkowe
1
patrz tutaj: pandas.pydata.org/pandas-docs/dev/… ; musisz mieć zmiennoprzecinkowy typ dtype, gdy u brakuje wartości (lub technicznie obiektowy typ dtype, ale jest to nieefektywne); jaki jest twój cel używania typu int?
Jeff
6
Uważam, że jest to problem NumPy, który nie dotyczy Pand. Szkoda, ponieważ istnieje tak wiele przypadków, w których typ int, który dopuszcza możliwość wartości null, jest znacznie bardziej wydajny niż duża kolumna zmiennoprzecinkowa.
ely
1
Ja też mam z tym problem. Mam wiele ramek danych, które chcę scalić w oparciu o ciąg reprezentujący kilka kolumn „całkowitych”. Jednak gdy jedna z tych kolumn liczb całkowitych ma np.nan, rzutowanie łańcucha generuje „.0”, co powoduje scalanie. Po prostu sprawia, że ​​rzeczy są nieco bardziej skomplikowane, byłoby miło, gdyby istniało proste obejście.
dermen
1
@Rhubarb, opcjonalna obsługa zerowych liczb całkowitych jest teraz oficjalnie dodana na pandach 0.24.0 - wreszcie :) - zaktualizowaną odpowiedź znajdziesz poniżej. pandy 0.24.x
mork

Odpowiedzi:

169

Brak powtórzeń NaN w kolumnach z liczbami całkowitymi to „łapacz” pandy .

Typowym obejściem jest po prostu użycie pływaków.

Andy Hayden
źródło
13
Czy są jakieś inne obejścia poza traktowaniem ich jak pływaków?
NumenorForLife
3
@ jsc123 możesz użyć obiektu dtype. Zawiera małe ostrzeżenie zdrowotne, ale w większości działa dobrze.
Andy Hayden
1
Czy możesz podać przykład używania typu obiektu? Przeglądałem dokumentację pand i googlowałem i przeczytałem, że to zalecana metoda. Ale nie znalazłem przykładu, jak używać dtype obiektu.
MikeyE
29
W wersji 0.24 możesz teraz zrobić df = df.astype(pd.Int32Dtype())(przekonwertować całą ramkę dataFrame lub) df['col'] = df['col'].astype(pd.Int32Dtype()). Inne akceptowane typy liczb całkowitych dopuszczających wartość null to pd.Int16Dtypei pd.Int64Dtype. Wybierz swoją truciznę.
cs95
1
Jest to wartość NaN, ale sprawdzanie isnan w ogóle nie działa :(
Winston,
117

W wersji 0.24. + Pandy zyskały możliwość przechowywania liczb całkowitych z brakami danych.

Typ danych typu Nullable Integer .

Pandy mogą reprezentować dane całkowite z prawdopodobnie brakującymi wartościami przy użyciu arrays.IntegerArray. To typy rozszerzeń zaimplementowane w pandach. Nie jest to domyślny typ dtype dla liczb całkowitych i nie zostanie on wywnioskowany; musisz jawnie przekazać dtype do array()lub Series:

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

0      1
1      2
2    NaN
dtype: Int64

Aby przekonwertować kolumnę na liczby całkowite dopuszczające wartość null, użyj:

df['myCol'] = df['myCol'].astype('Int64')
jezrael
źródło
4
Podoba mi się ta odpowiedź.
cs95
8
Pamiętaj, że dtype musi być, "Int64"a nie "int64"(pierwsze „i” musi być pisane wielką literą)
Viacheslav Z
2
df.myCol = df.myCol.astype('Int64')lubdf['myCol'] = df['myCol'].astype('Int64')
LoMaPh
43

Mój przypadek użycia polega na łączeniu danych przed załadowaniem do tabeli bazy danych:

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)

Usuń NaN, przekonwertuj na int, przekonwertuj na str, a następnie ponownie włóż NAN.

Nie jest ładna, ale spełnia swoje zadanie!

hibernado
źródło
1
Wyciągałem włosy z głowy, próbując załadować numery seryjne, w których niektóre są puste, a reszta to pływaki, to mnie uratowało.
Chris Decker
1
OP potrzebuje kolumny liczb całkowitych. Przekształcenie go na łańcuch nie spełnia warunku.
Rishab Gupta,
1
Działa tylko wtedy, gdy kolumna nie ma już -1. W przeciwnym razie będzie bałagan z danymi
Sharvari Gc
to jak wrócić do int .. ??
abdoulsn
5

Teraz można utworzyć kolumnę pandy zawierającą NaN jako dtype int, ponieważ jest ona teraz oficjalnie dodawana na pandach 0.24.0

pandas 0.24.x - informacje o wydaniu Cytat: „ Pandy zyskały zdolność do przechowywania liczb całkowitych z brakującymi wartościami

mork
źródło
4

Jeśli absolutnie chcesz połączyć liczby całkowite i NaN w kolumnie, możesz użyć typu danych „obiekt”:

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

Spowoduje to zastąpienie NaN liczbą całkowitą (nie ma znaczenia która), konwersję na int, konwersję na obiekt i na końcu ponowne wstawienie NaN.

jmenglund
źródło
3

Jeśli możesz zmodyfikować zapisane dane, użyj wartości wartowniczej dla brakujących danych id. Typowy przypadek użycia, wywodzący się z nazwy kolumny, będący idliczbą całkowitą, ściśle większą od zera, można użyć 0jako wartości wartowniczej, aby można było pisać

if row['id']:
   regular_process(row)
else:
   special_process(row)
gboffi
źródło
3

Możesz użyć, .dropna()jeśli możesz usunąć wiersze z wartościami NaN.

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

Alternatywnie użyj .fillna()i.astype() aby zastąpić NaN wartościami i przekonwertować je na int.

Napotkałem ten problem podczas przetwarzania pliku CSV z dużymi liczbami całkowitymi, podczas gdy niektórych z nich brakowało (NaN). Używanie typu float nie wchodziło w grę, ponieważ mógłbym stracić precyzję.

Moim rozwiązaniem było użycie str jako typu pośredniego . Następnie możesz przekonwertować ciąg na int, jak chcesz później w kodzie. Zastąpiłem NaN 0, ale możesz wybrać dowolną wartość.

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

Dla ilustracji, oto przykład, w jaki sposób zmiennoprzecinkowe mogą stracić precyzję:

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

A wynik to:

1.2345678901234567e+19 12345678901234567168 12345678901234567890
elomage
źródło
2

Większość rozwiązań tutaj mówi, jak używać zastępczej liczby całkowitej do reprezentowania wartości null. Takie podejście nie jest pomocne, jeśli nie masz pewności, że liczba całkowita nie pojawi się w danych źródłowych. Moja metoda z formatuje zmiennoprzecinkowe bez ich wartości dziesiętnych i konwertuje wartości null na brak. Wynikiem jest obiektowy typ danych, który po załadowaniu do pliku CSV będzie wyglądał jak pole typu integer z wartościami null.

keep_df[col] = keep_df[col].apply(lambda x: None if pandas.isnull(x) else '{0:.0f}'.format(pandas.to_numeric(x)))
Corbin
źródło
1

Napotkałem ten problem podczas pracy z pyspark. Ponieważ jest to nakładka na język Pythona dla kodu działającego w jvm, wymaga bezpieczeństwa typów, a używanie float zamiast int nie jest opcją. Rozwiązałem ten problem, opakowując pandy pd.read_csvw funkcję, która wypełni kolumny zdefiniowane przez użytkownika wartościami wypełnienia zdefiniowanymi przez użytkownika przed rzutowaniem ich na wymagany typ. Oto, czego ostatecznie użyłem:

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
źródło
1
import pandas as pd

df= pd.read_csv("data.csv")
df['id'] = pd.to_numeric(df['id'])
Monaheng Ramochele
źródło
4
Czy jest jakiś powód, dla którego wolisz to sformułowanie od proponowanego w zaakceptowanej odpowiedzi? Jeśli tak, dobrze byłoby zmodyfikować swoją odpowiedź, aby podać to wyjaśnienie - a zwłaszcza, że ​​istnieje dziesięć dodatkowych odpowiedzi, które rywalizują o uwagę.
Jeremy Caney
Chociaż ten kod może rozwiązać problem z OP, najlepiej jest dołączyć wyjaśnienie, w jaki sposób / dlaczego Twój kod go rozwiązuje. W ten sposób przyszli odwiedzający mogą uczyć się z Twojego postu i zastosować go do własnego kodu. SO nie jest usługą programistyczną, ale źródłem wiedzy. Również wysokiej jakości, kompletne odpowiedzi mają większe szanse na głosowanie. Te funkcje, wraz z wymogiem, że wszystkie posty są niezależne, są jednymi z mocnych stron SO, ponieważ platforma odróżnia ją od forów. Możesz editdodać dodatkowe informacje i / lub uzupełnić swoje wyjaśnienia o dokumentację źródłową.
SherylHohman
0

Najpierw usuń wiersze zawierające NaN. Następnie wykonaj konwersję liczb całkowitych w pozostałych wierszach. W końcu wstaw ponownie usunięte wiersze. Mam nadzieję, że to zadziała

kamran kausar
źródło
-1

Zakładając, że kolumna DateColumn sformatowana 3312018.0 powinna zostać przekonwertowana na 31.03.2018 jako ciąg. Brakuje niektórych rekordów lub 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
źródło