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.
9 answers
Masz cztery główne opcje konwersji typów w pandach:
-
to_numeric()
- umożliwia bezpieczną konwersję typów nieliczbowych (np. ciągów znaków) na odpowiedni typ numeryczny. (Zobacz teżto_datetime()
orazto_timedelta()
.) -
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). -
infer_objects()
- metoda użytkowa do konwersji kolumn obiektów zawierających obiekty Pythona na typ pandy, jeśli to możliwe. -
convert_dtypes()
- Konwertuj kolumny ramki danych na "najlepszy możliwy" Typ dtype, który obsługujepd.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ą.
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".
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
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.
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'})
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'])
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.
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
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.
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
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
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