Zmień typ danych kolumn w Pandach

804

Chcę przekonwertować tabelę reprezentowaną jako lista list na plik Pandas DataFrame. Jako wyjątkowo uproszczony przykład:

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a)

Jaki jest najlepszy sposób przekonwertowania kolumn na odpowiednie typy, w tym przypadku kolumny 2 i 3 na zmiennoprzecinkowe? Czy istnieje sposób określenia typów podczas konwersji do DataFrame? A może lepiej najpierw utworzyć DataFrame, a następnie przejść przez kolumny, aby zmienić typ każdej kolumny? Idealnie chciałbym to zrobić w sposób dynamiczny, ponieważ mogą istnieć setki kolumn i nie chcę dokładnie określać, które kolumny są tego typu. Mogę tylko zagwarantować, że każda kolumna zawiera wartości tego samego typu.

Socjopata
źródło
Widziałem metody konwertowania każdej kolumny i metody konwertowania kolumn o nazwanych nazwach, ale co powiesz na niektóre kolumny, które spełniają określony warunek, gdy nie możesz wyświetlić 100 kolumn, które chcesz przekonwertować jednocześnie? Mam na myśli na przykład wszystkie float64 -> float32 lub inne taktyki oszczędzania pamięci.
demongolem
@demongolem: możesz zrobić coś takiego, jak sprowadzić df.apply(pd.to_numeric, downcast="integer", errors="ignore")kolumny liczb całkowitych do najmniejszego (całkowitego) typu, który będzie przechowywał wartości.
Alex Riley

Odpowiedzi:

1190

Masz trzy główne opcje konwersji typów w pandach:

  1. to_numeric()- zapewnia funkcjonalność pozwalającą bezpiecznie konwertować typy nienumeryczne (np. łańcuchy) na odpowiedni typ numeryczny. (Zobacz także to_datetime()i to_timedelta().)

  2. astype()- konwertuje (prawie) dowolny typ na (prawie) dowolny inny typ (nawet jeśli niekoniecznie jest to rozsądne). Umożliwia także konwersję na typy kategorialne (bardzo przydatne).

  3. infer_objects() - metoda użyteczna do konwersji kolumn obiektów zawierających obiekty Python na typ pandy, jeśli to możliwe.

Czytaj dalej, aby uzyskać bardziej szczegółowe wyjaśnienia i zastosowanie każdej z tych metod.


1. to_numeric()

Najlepszym sposobem konwersji jednej lub więcej kolumn DataFrame na wartości liczbowe jest użycie pandas.to_numeric().

Ta funkcja spróbuje zmienić obiekty nienumeryczne (takie jak łańcuchy) na liczby całkowite lub liczby zmiennoprzecinkowe, odpowiednio.

Podstawowe użycie

Dane wejściowe to_numeric()to Seria lub pojedyncza kolumna DataFrame.

>>> s = pd.Series(["8", 6, "7.5", 3, "0.9"]) # mixed string and numeric values
>>> s
0      8
1      6
2    7.5
3      3
4    0.9
dtype: object

>>> pd.to_numeric(s) # convert everything to float values
0    8.0
1    6.0
2    7.5
3    3.0
4    0.9
dtype: float64

Jak widać, zwracana jest nowa seria. Pamiętaj, aby przypisać to wyjście do nazwy zmiennej lub kolumny, aby dalej z niego korzystać:

# convert Series
my_series = pd.to_numeric(my_series)

# convert column "a" of a DataFrame
df["a"] = pd.to_numeric(df["a"])

Można go również użyć do konwersji wielu kolumn DataFrame za pomocą apply()metody:

# convert all columns of DataFrame
df = df.apply(pd.to_numeric) # convert all columns of DataFrame

# convert just columns "a" and "b"
df[["a", "b"]] = df[["a", "b"]].apply(pd.to_numeric)

Tak długo, jak wszystkie wartości można przekonwertować, prawdopodobnie to wszystko, czego potrzebujesz.

Obsługa błędów

Ale co, jeśli niektórych wartości nie można przekonwertować na typ liczbowy?

to_numeric()bierze również errorsargument słowa kluczowego, który pozwala na wymuszenie wartości nienumerycznych NaNlub po prostu zignorować kolumny zawierające te wartości.

Oto przykład z wykorzystaniem serii ciągów, sktóre mają obiekt dtype:

>>> s = pd.Series(['1', '2', '4.7', 'pandas', '10'])
>>> s
0         1
1         2
2       4.7
3    pandas
4        10
dtype: object

Domyślnym zachowaniem jest podniesienie, jeśli nie można przekonwertować wartości. W tym przypadku nie radzi sobie z ciągiem „pandy”:

>>> pd.to_numeric(s) # or pd.to_numeric(s, errors='raise')
ValueError: Unable to parse string

Zamiast zawieść, możemy chcieć, aby „pandy” były traktowane jako brakująca / zła wartość liczbowa. Możemy zmusić niepoprawne wartości do NaNnastępujących wartości, używając errorsargumentu słowa kluczowego:

>>> pd.to_numeric(s, errors='coerce')
0     1.0
1     2.0
2     4.7
3     NaN
4    10.0
dtype: float64

Trzecią opcją errorsjest zignorowanie operacji, jeśli napotkamy niepoprawną wartość:

>>> pd.to_numeric(s, errors='ignore')
# the original Series is returned untouched

Ta ostatnia opcja jest szczególnie przydatna, gdy chcesz przekonwertować całą ramkę danych, ale nie wiesz, która z naszych kolumn może być niezawodnie przekonwertowana na typ liczbowy. W takim przypadku po prostu napisz:

df.apply(pd.to_numeric, errors='ignore')

Funkcja zostanie zastosowana do każdej kolumny DataFrame. Kolumny, które można przekonwertować na typ liczbowy, zostaną przekonwertowane, a kolumny, które nie będą mogły (np. Zawierają niecyfrowe ciągi lub daty) zostaną pozostawione same.

Downcasting

Domyślnie konwersja z to_numeric()da ci albo a int64lub float64dtype (lub dowolną szerokość całkowitą natywną dla twojej platformy).

Zwykle tego chcesz, ale co jeśli chcesz zaoszczędzić trochę pamięci i użyć bardziej zwartego typu, np. float32Lub int8?

to_numeric()daje opcję spuszczania do „liczb całkowitych”, „podpisanych”, „niepodpisanych”, „zmiennoprzecinkowych”. Oto przykład prostej serii sliczb całkowitych:

>>> s = pd.Series([1, 2, -7])
>>> s
0    1
1    2
2   -7
dtype: int64

Downcasting do „integer” używa najmniejszej możliwej liczby całkowitej, która może przechowywać wartości:

>>> pd.to_numeric(s, downcast='integer')
0    1
1    2
2   -7
dtype: int8

Downcasting do „float” podobnie wybiera mniejszy niż normalny typ pływający:

>>> pd.to_numeric(s, downcast='float')
0    1.0
1    2.0
2   -7.0
dtype: float32

2) astype()

Ta astype()metoda umożliwia wyraźne określenie typu, jaki ma mieć DataFrame lub seria. Jest bardzo wszechstronny, ponieważ możesz próbować przejść od jednego typu do drugiego.

Podstawowe użycie

Po prostu wybierz typ: możesz użyć typu NumPy (np. np.int16), Niektórych typów Python (np. Bool) lub typów specyficznych dla pand (takich jak typowy typ).

Wywołaj metodę na obiekcie, który chcesz przekonwertować, i astype()spróbuje go przekonwertować:

# convert all DataFrame columns to the int64 dtype
df = df.astype(int)

# convert column "a" to int64 dtype and "b" to complex type
df = df.astype({"a": int, "b": complex})

# convert Series to float16 type
s = s.astype(np.float16)

# convert Series to Python strings
s = s.astype(str)

# convert Series to categorical type - see docs for more details
s = s.astype('category')

Zauważ, że powiedziałem „spróbuj” - jeśli astype()nie wie, jak przekonwertować wartość w Series lub DataFrame, zgłosi błąd. Na przykład, jeśli masz wartość NaNlub, infzobaczysz błąd podczas próby konwersji jej na liczbę całkowitą.

Począwszy od pand 0.20.0, błąd ten można stłumić, przekazując errors='ignore'. Twój oryginalny obiekt zostanie nienaruszony.

Bądź ostrożny

astype()jest potężny, ale czasami konwertuje wartości „niepoprawnie”. Na przykład:

>>> s = pd.Series([1, 2, -7])
>>> s
0    1
1    2
2   -7
dtype: int64

Są to małe liczby całkowite, więc co powiesz na konwersję na niepodpisany 8-bitowy typ w celu oszczędzania pamięci?

>>> s.astype(np.uint8)
0      1
1      2
2    249
dtype: uint8

Konwersja zadziałała, ale wartość -7 została zawinięta na 249 (tj. 2 8–7 )!

Próba użycia downcast przy użyciu pd.to_numeric(s, downcast='unsigned')zamiast tego może pomóc zapobiec temu błędowi.


3) infer_objects()

W wersji 0.21.0 pand wprowadzono metodę infer_objects()konwersji kolumn DataFrame, które mają typ danych obiektu na bardziej konkretny typ (konwersje miękkie).

Na przykład, oto DataFrame z dwiema kolumnami typu obiektu. Jedna zawiera rzeczywiste liczby całkowite, a druga ciągi znaków reprezentujące liczby całkowite:

>>> df = pd.DataFrame({'a': [7, 1, 5], 'b': ['3','2','1']}, dtype='object')
>>> df.dtypes
a    object
b    object
dtype: object

Za pomocą infer_objects()możesz zmienić typ kolumny „a” na int64:

>>> df = df.infer_objects()
>>> df.dtypes
a     int64
b    object
dtype: object

Kolumna „b” została pozostawiona sama, ponieważ jej wartości były łańcuchami, a nie liczbami całkowitymi. Jeśli chcesz spróbować wymusić konwersję obu kolumn na typ całkowity, możesz df.astype(int)zamiast tego użyć .

Alex Riley
źródło
8
Ponadto, w przeciwieństwie do .astype (float), spowoduje to konwersję ciągów znaków na NaN zamiast zgłaszania błędu
Rob
11
.convert_objectsjest depracowany, ponieważ 0.17- df.to_numericzamiast tego użyj
Matti Lyra
4
Dzięki - powinienem zaktualizować tę odpowiedź. Być może warto to zauważyć, pd.to_numerica metody towarzyszące będą działać tylko na jednej kolumnie na raz, w przeciwieństwie do convert_objects. Trwa dyskusja na temat funkcji zamiany w interfejsie API ; Mam nadzieję, że metoda, która działa w całej DataFrame, pozostanie, ponieważ jest bardzo przydatna.
Alex Riley
Jaki jest najlepszy sposób przekonwertować wszystkie kolumny, które są obecnie, powiedzmy, int64do int32?
RoyalTS
4
@RoyalTS: prawdopodobnie najlepiej użyć astype(jak w drugiej odpowiedzi), tj .astype(numpy.int32).
Alex Riley,
447

Co powiesz na to?

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])
df
Out[16]: 
  one  two three
0   a  1.2   4.2
1   b   70  0.03
2   x    5     0

df.dtypes
Out[17]: 
one      object
two      object
three    object

df[['two', 'three']] = df[['two', 'three']].astype(float)

df.dtypes
Out[19]: 
one       object
two      float64
three    float64
hernamesbarbara
źródło
10
Tak! pd.DataFramema dtypeargument, który może ci pomóc w / szukasz. df = pd.DataFrame (a, columns = ['one', 'two', 'three'], dtype = float) In [2]: df.dtypes Out [2]: jeden obiekt dwa float64 trzy float64 dtype: obiekt
hernamesbarbara,
17
Kiedy próbuję zgodnie z sugestią, pojawia się ostrzeżenie SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_index,col_indexer] = value instead. Być może zostało to wprowadzone w nowszej wersji pand i dlatego nie widzę w tym nic złego, ale po prostu zastanawiam się, o co chodzi w tym ostrzeżeniu. Dowolny pomysł?
pomarańczowy
2
@orange ostrzeżenie ma ostrzegać użytkowników przed potencjalnie mylącym zachowaniem z połączonymi operacjami i pandami zwracającymi kopie zamiast edytować ramki danych. patrz stackoverflow.com/questions/20625582/... i powiązane.
A.Wan
19
To dobra metoda, ale nie działa, gdy w kolumnie znajduje się NaN. Nie mam pojęcia, dlaczego NaN po prostu nie może pozostać NaN podczas rzucania float na int:ValueError: Cannot convert NA to integer
Witalij Isaev
7
@GillBates tak, w słowniku. df = pd.DataFrame(a, columns=['one', 'two', 'three'], dtype={'one': str, 'two': int, 'three': float}). Trudno mi jednak znaleźć specyfikację dla akceptowanych wartości „dtype”. Lista byłaby miła (obecnie to robię dict(enumerate(my_list))).
FichteFoll,
39

ten poniższy kod zmieni typ danych kolumny.

df[['col.name1', 'col.name2'...]] = df[['col.name1', 'col.name2'..]].astype('data_type')

zamiast typu danych możesz podać swój typ danych. czego chcesz, jak str, float, int itp.

Akash Nayak
źródło
Pamiętaj, że po zastosowaniu tego w kolumnie zawierającej ciągi znaków `` True '' i `` 'False' '`za pomocą data_type bool, wszystko zmienia się na True.
H. Vabri
Tę opcję możesz również przekonwertować na typ „kategoria”
neves
17

Kiedy muszę tylko określić konkretne kolumny i chcę wyrazić się jasno, użyłem (według LOKALIZACJI DOCS ):

dataframe = dataframe.astype({'col_name_1':'int','col_name_2':'float64', etc. ...})

Korzystając z oryginalnego pytania, ale podając do niego nazwy kolumn ...

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['col_name_1', 'col_name_2', 'col_name_3'])
df = df.astype({'col_name_2':'float64', 'col_name_3':'float64'})
Thom Ives
źródło
15

Oto funkcja, która bierze za argumenty ramkę danych i listę kolumn i wymusza wszystkie dane w kolumnach na liczby.

# df is the DataFrame, and column_list is a list of columns as strings (e.g ["col1","col2","col3"])
# dependencies: pandas

def coerce_df_columns_to_numeric(df, column_list):
    df[column_list] = df[column_list].apply(pd.to_numeric, errors='coerce')

Na przykład:

import pandas as pd

def coerce_df_columns_to_numeric(df, column_list):
    df[column_list] = df[column_list].apply(pd.to_numeric, errors='coerce')

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['col1','col2','col3'])

coerce_df_columns_to_numeric(df, ['col2','col3'])
Harry Stevens
źródło
co jeśli chcesz użyć indeksów kolumn zamiast nazw kolumn?
jvalenti
8

Co powiesz na utworzenie dwóch ramek danych, z których każdy ma inny typ danych dla swoich kolumn, a następnie dołączenie ich razem?

d1 = pd.DataFrame(columns=[ 'float_column' ], dtype=float)
d1 = d1.append(pd.DataFrame(columns=[ 'string_column' ], dtype=str))

Wyniki

In[8}:  d1.dtypes
Out[8]: 
float_column     float64
string_column     object
dtype: object

Po utworzeniu ramki danych możesz wypełnić ją zmiennymi zmiennoprzecinkowymi w pierwszej kolumnie, a ciągi (lub dowolny typ danych, który chcesz) w drugiej kolumnie.

MikeyE
źródło
4

pandy> = 1,0

Oto wykres podsumowujący niektóre z najważniejszych konwersji w pandach.

wprowadź opis zdjęcia tutaj

Konwersje na ciąg są trywialne .astype(str)i nie są pokazane na rysunku.

Konwersje „twarde” kontra „miękkie”

Zauważ, że „konwersje” w tym kontekście mogą odnosić się do konwertowania danych tekstowych na ich rzeczywisty typ danych (konwersja twarda) lub wnioskowania o bardziej odpowiednich typach danych dla danych w kolumnach obiektowych (konwersja miękka). Aby zilustrować różnicę, spójrz na

df = pd.DataFrame({'a': ['1', '2', '3'], 'b': [4, 5, 6]}, dtype=object)
df.dtypes                                                                  

a    object
b    object
dtype: object

# Actually converts string to numeric - hard conversion
df.apply(pd.to_numeric).dtypes                                             

a    int64
b    int64
dtype: object

# Infers better data types for object data - soft conversion
df.infer_objects().dtypes                                                  

a    object  # no change
b     int64
dtype: object

# Same as infer_objects, but converts to equivalent ExtensionType
df.convert_dtypes().dtypes                                                     
cs95
źródło
1

Myślałem, że mam ten sam problem, ale w rzeczywistości mam niewielką różnicę, która sprawia, że ​​problem jest łatwiejszy do rozwiązania. Dla innych, którzy patrzą na to pytanie, warto sprawdzić format listy wejściowej. W moim przypadku liczby są początkowo liczbami zmiennoprzecinkowymi, a nie ciągami, jak w pytaniu:

a = [['a', 1.2, 4.2], ['b', 70, 0.03], ['x', 5, 0]]

ale przetwarzając listę zbyt dużo przed utworzeniem ramki danych, tracę typy i wszystko staje się ciągiem.

Tworzenie ramki danych za pomocą tablicy numpy

df = pd.DataFrame(np.array(a))

df
Out[5]: 
   0    1     2
0  a  1.2   4.2
1  b   70  0.03
2  x    5     0

df[1].dtype
Out[7]: dtype('O')

daje taką samą ramkę danych jak w pytaniu, gdzie wpisy w kolumnach 1 i 2 są uważane za ciągi. Jednak robiąc

df = pd.DataFrame(a)

df
Out[10]: 
   0     1     2
0  a   1.2  4.20
1  b  70.0  0.03
2  x   5.0  0.00

df[1].dtype
Out[11]: dtype('float64')

faktycznie daje ramkę danych z kolumnami w odpowiednim formacie

SarahD
źródło
0

Począwszy od pand 1.0.0, mamy pandas.DataFrame.convert_dtypes. Możesz nawet kontrolować, jakie typy konwertować!

In [40]: df = pd.DataFrame(
    ...:     {
    ...:         "a": pd.Series([1, 2, 3], dtype=np.dtype("int32")),
    ...:         "b": pd.Series(["x", "y", "z"], dtype=np.dtype("O")),
    ...:         "c": pd.Series([True, False, np.nan], dtype=np.dtype("O")),
    ...:         "d": pd.Series(["h", "i", np.nan], dtype=np.dtype("O")),
    ...:         "e": pd.Series([10, np.nan, 20], dtype=np.dtype("float")),
    ...:         "f": pd.Series([np.nan, 100.5, 200], dtype=np.dtype("float")),
    ...:     }
    ...: )

In [41]: dff = df.copy()

In [42]: df 
Out[42]: 
   a  b      c    d     e      f
0  1  x   True    h  10.0    NaN
1  2  y  False    i   NaN  100.5
2  3  z    NaN  NaN  20.0  200.0

In [43]: df.dtypes
Out[43]: 
a      int32
b     object
c     object
d     object
e    float64
f    float64
dtype: object

In [44]: df = df.convert_dtypes()

In [45]: df.dtypes
Out[45]: 
a      Int32
b     string
c    boolean
d     string
e      Int64
f    float64
dtype: object

In [46]: dff = dff.convert_dtypes(convert_boolean = False)

In [47]: dff.dtypes
Out[47]: 
a      Int32
b     string
c     object
d     string
e      Int64
f    float64
dtype: object
Sohail
źródło