Zmień typ kolumny w pandy

Chcę przekonwertować tabelę, reprezentowaną jako lista list, na Pandas DataFrame. Jako niezwykle 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 na konwersję kolumn na odpowiednie typy, w tym przypadku kolumny 2 i 3 na pływaki? Czy istnieje sposób na określenie typów podczas konwersji do ramki danych? A może lepiej najpierw utworzyć ramkę danych, a następnie zapętlić kolumny, aby zmienić typ dla każdej kolumny? Idealnie chciałbym to zrobić w dynamiczny sposób ponieważ mogą być setki kolumn i nie chcę dokładnie określać, które kolumny są tego typu. Wszystko, co mogę zagwarantować, to to, że każda kolumna zawiera wartości tego samego typu.

Author: Trenton McKinney, 2013-04-08

9 answers

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

  1. to_numeric() - umożliwia bezpieczną konwersję typów nieliczbowych (np. ciągów znaków) na odpowiedni typ numeryczny. (Zobacz też to_datetime() oraz to_timedelta().)

  2. astype() - Konwertuj (prawie) dowolny typ na (prawie) dowolny inny typ(nawet jeśli nie jest to koniecznie sensowne). Pozwala również na konwersję do typów (bardzo przydatne).

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

  4. convert_dtypes() - Konwertuj kolumny ramki danych na "najlepszy możliwy" Typ dtype, który obsługuje pd.NA (obiekt pandas, aby wskazać brakującą wartość).

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


1. to_numeric()

Najlepszy sposób na konwersję jednego lub więcej kolumn ramki danych do wartości liczbowych służy pandas.to_numeric().

Ta funkcja spróbuje zmienić obiekty nieliczbowe (takie jak łańcuchy znaków) na liczby całkowite lub liczby zmiennoprzecinkowe.

Podstawowe użycie

Wejście do to_numeric() jest serią lub pojedynczą kolumną ramki danych.

>>> 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ć, powraca nowa seria. Pamiętaj, aby przypisać to wyjście do zmiennej lub nazwy kolumny, aby kontynuować korzystanie z niego:

# convert Series
my_series = pd.to_numeric(my_series)

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

Możesz także można go użyć do konwersji wielu kolumn ramki danych za pomocą metody apply():

# 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 twoje wartości mogą być konwertowane, to prawdopodobnie wszystko, czego potrzebujesz.

Obsługa błędów

Ale co jeśli niektóre wartości nie mogą być przekonwertowane na typ liczbowy?

to_numeric() przyjmuje również argument słowa kluczowego errors, który pozwala wymusić wartości nieliczbowe na NaN, lub po prostu ignorować kolumny zawierające te wartości.

Oto przykład użycia serii ciągów s który posiada 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 może poradzić sobie z ciągiem "pandas": {]}

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

Zamiast niepowodzenia, możemy chcieć, aby 'pandy' były uważane za brakującą / złą wartość liczbową. Możemy zmusić nieprawidłowe wartości do NaN w następujący sposób używając argumentu słowa kluczowego errors:

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

Trzecią opcją dla errors jest ignorowanie operacji, jeśli napotkana zostanie Nieprawidłowa wartość:

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

Ten ostatni opcja jest szczególnie przydatna, gdy chcesz przekonwertować całą ramkę danych, ale nie wiesz, które z naszych kolumn można przekonwertować niezawodnie na typ numeryczny. W takim przypadku po prostu napisz:

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

Funkcja zostanie zastosowana do każdej kolumny ramki danych. Kolumny, które mogą być przekonwertowane na typ numeryczny, zostaną przekonwertowane, podczas gdy kolumny, które nie mogą (np. zawierają niecyfrowe ciągi znaków lub daty) zostaną pozostawione same.

Downcasting

Domyślnie konwersja z to_numeric() da ci int64 lub float64 dtype (lub dowolna szerokość Całkowita jest natywna dla Twojej platformy).

Zazwyczaj tego chcesz, ale co, jeśli chcesz zaoszczędzić trochę pamięci i użyć bardziej kompaktowego typu dtype, jak float32 lub int8?

to_numeric() daje możliwość downcast do 'integer', 'signed', 'unsigned', 'float'. Oto przykład prostej serii s typu integer:

>>> 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()

The astype() metoda pozwala jasno określić typ dtype, który ma mieć ramka danych lub seria. Jest bardzo wszechstronny, ponieważ można próbować przejść od jednego typu do dowolnego innego.

Podstawowe użycie

Po prostu wybierz typ: możesz użyć NumPy dtype (np. np.int16), niektórych typów Pythona (np. bool), lub pandy-specyficzne typy (jak kategoryczny dtype).

Wywołanie metody na obiekcie, który chcesz przekonwertować, a astype() spróbuje ją przekonwertować za Ciebie:

# 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')

Uwaga powiedziałem "spróbuj" - jeśli astype() nie wie, jak przekonwertować wartość z serii lub ramki danych, spowoduje to błąd. Na przykład, jeśli masz wartość NaN lub inf, pojawi się błąd podczas próby przekonwertowania jej na liczbę całkowitą.

[[70]}od wersji pandas 0.20.0, ten błąd można stłumić przekazując errors='ignore'. Twój oryginalny obiekt zostanie zwrócony nietknięty.

Bądź ostrożny

astype() jest potężny, ale czasami konwertuje wartości "nieprawidłowo". 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ę do niepodpisanego typu 8-bitowego, aby zapisać pamięć?

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

Konwersja zadziałała, ale -7 było owinięte wokół, aby stać się 249 (tj. 28 - 7)!

Próba downcast za pomocą pd.to_numeric(s, downcast='unsigned') zamiast tego może pomóc zapobiec temu błędowi.


3. infer_objects()

Wersja 0.21.0 pandy wprowadziła metodę infer_objects() do konwersji kolumn ramki danych, które mają obiektowy typ danych, na bardziej konkretny typ (miękkie konwersje).

Na przykład, oto ramka danych z dwiema kolumnami typu object. Jeden posiada rzeczywiste liczby całkowite, a drugi posiada ciągi 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

Używając 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 od czasu jej wartości były ciągami, a nie liczbami całkowitymi. Jeśli chcesz spróbować wymusić konwersję obu kolumn do typu integer, możesz zamiast tego użyć df.astype(int).


4. convert_dtypes()

Wersja 1.0 i wyżej zawiera metodę convert_dtypes() aby przekonwertować kolumny serii i ramki danych na najlepszy możliwy Typ dtype obsługujący brakującą wartość pd.NA.

Tutaj "best possible" oznacza typ najbardziej odpowiedni do przechowywania wartości. Na przykład, to jest typ typu integer, jeśli wszystkie wartości są liczbami całkowitymi( lub brakującymi wartościami): kolumna obiektu obiektów liczb całkowitych Pythona jest konwertowana na Int64, kolumna wartości NumPy int32 stanie się dtype pandas Int32.

Z naszego object DataFrame df otrzymujemy następujący wynik:

>>> df.convert_dtypes().dtypes                                             
a     Int64
b    string
dtype: object

Ponieważ kolumna ' a ' przechowywała wartości całkowite, została przekonwertowana na typ Int64 (który może przechowywać brakujące wartości, w przeciwieństwie do int64).

Kolumna 'b' zawierała obiekty łańcuchowe, więc została zmieniona na pandas' string dtype.

Domyślnie metoda ta wywnioskuje typ z wartości obiektu w każdej kolumnie. Możemy to zmienić przechodząc infer_objects=False:

>>> df.convert_dtypes(infer_objects=False).dtypes                          
a    object
b    string
dtype: object

Teraz kolumna ' a 'pozostała kolumną obiektową: pandas wie, że może być opisana jako kolumna 'integer' (wewnętrznie biegła infer_dtype) ale nie wywnioskował dokładnie, jaki dtype integer powinien mieć, więc go nie przekonwertował. Kolumna " b "została ponownie przekonwertowana na "string" dtype, ponieważ została uznana za posiadającą wartości "string".

 1536
Author: Alex Riley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-09 14:42:51

Co ty 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
 461
Author: hernamesbarbara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-19 13:39:59

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 .co chcesz jak str, float, int itp.

 42
Author: Akash Nayak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-15 09:38:01

Kiedy potrzebowałem tylko określić konkretne kolumny i chcę być jawny, użyłem (per DOCS LOCATION):

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

Więc, używając oryginalnego pytania, ale podając mu 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'})
 21
Author: Thom Ives,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-12 21:02:28

Tutaj jest funkcja, która przyjmuje jako swoje argumenty ramkę danych i listę kolumn i zmusza wszystkie dane w kolumnach do liczb.

# 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')

Więc, dla Twojego przykładu:

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'])
 15
Author: Harry Stevens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-14 05:42:21

Pandy >= 1.0

Oto wykres, który podsumowuje niektóre z najważniejszych konwersji w pandach.

Tutaj wpisz opis obrazka

Konwersje na string 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 konwersji danych tekstowych na ich rzeczywisty typ danych (twarda konwersja) lub wnioskowania bardziej odpowiednich typów danych dla danych w kolumnach obiektów (miękka konwersja). 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                                                     
 10
Author: cs95,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-19 06:27:00

Co powiesz na utworzenie dwóch ramek danych, każdy z różnymi typami danych dla swoich kolumn, a następnie dodanie 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żna ją wypełnić zmiennoprzecinkowymi zmiennymi w pierwszej kolumnie, a łańcuchami (lub dowolnymi typami danych) w drugiej kolumnie.

 8
Author: MikeyE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-11 05:56:48

Począwszy od pandas 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
 3
Author: Sohail,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-06 14:26:33

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 patrzących na to pytanie warto sprawdzić format listy wejściowej. W moim przypadku liczby są początkowo pływakami, a nie ciągami jak w pytaniu:

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

Ale zbytnio przetwarzając listę przed utworzeniem ramki danych, tracę typy i wszystko staje się ciągiem znaków.

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 ta sama ramka danych, co w pytaniu, gdzie wpisy w kolumnach 1 i 2 są traktowane jako ciągi znaków. Jednak robi

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

 1
Author: SarahD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-01 09:49:06