Ändern Sie den Datentyp der Spalten in Pandas

805

Ich möchte eine Tabelle, die als Liste von Listen dargestellt wird, in eine konvertieren Pandas DataFrame. Als extrem vereinfachtes Beispiel:

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

Was ist der beste Weg, um die Spalten in die entsprechenden Typen zu konvertieren, in diesem Fall die Spalten 2 und 3 in Gleitkommazahlen? Gibt es eine Möglichkeit, die Typen beim Konvertieren in DataFrame anzugeben? Oder ist es besser, zuerst den DataFrame zu erstellen und dann die Spalten zu durchlaufen, um den Typ für jede Spalte zu ändern? Idealerweise möchte ich dies auf dynamische Weise tun, da es Hunderte von Spalten geben kann und ich nicht genau angeben möchte, welche Spalten von welchem ​​Typ sind. Ich kann nur garantieren, dass jede Spalte Werte des gleichen Typs enthält.

Soziopath
quelle
Ich habe Ansätze zum Konvertieren jeder Spalte und Ansätze zum Konvertieren von speziell benannten Spalten gesehen, aber wie wäre es mit bestimmten Spalten, die eine bestimmte Bedingung erfüllen, wenn Sie nicht 100 Spalten auflisten können, die Sie gleichzeitig konvertieren möchten? Ich denke zum Beispiel an alle float64 -> float32 oder andere speichersparende Taktiken.
Demongolem
@demongolem: Sie könnten so etwas wie df.apply(pd.to_numeric, downcast="integer", errors="ignore")Integer-Spalten auf den kleinsten (Integer-) D-Typ herabsetzen, der die Werte enthält.
Alex Riley

Antworten:

1190

Sie haben drei Hauptoptionen zum Konvertieren von Typen in Pandas:

  1. to_numeric()- bietet Funktionen zum sicheren Konvertieren nicht numerischer Typen (z. B. Zeichenfolgen) in einen geeigneten numerischen Typ. (Siehe auch to_datetime()und to_timedelta().)

  2. astype()- (fast) jeden Typ in (fast) jeden anderen Typ konvertieren (auch wenn dies nicht unbedingt sinnvoll ist). Ermöglicht auch das Konvertieren in Kategorietypen (sehr nützlich).

  3. infer_objects() - eine Dienstprogrammmethode zum Konvertieren von Objektspalten, die Python-Objekte enthalten, wenn möglich in einen Pandas-Typ.

Lesen Sie weiter, um detailliertere Erklärungen und die Verwendung jeder dieser Methoden zu erhalten.


1. to_numeric()

Der beste Weg, eine oder mehrere Spalten eines DataFrame in numerische Werte zu konvertieren, ist die Verwendung pandas.to_numeric().

Diese Funktion versucht, nicht numerische Objekte (z. B. Zeichenfolgen) nach Bedarf in Ganzzahlen oder Gleitkommazahlen umzuwandeln.

Grundlegende Verwendung

Die Eingabe in to_numeric()ist eine Serie oder eine einzelne Spalte eines 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

Wie Sie sehen können, wird eine neue Serie zurückgegeben. Denken Sie daran, diese Ausgabe einem Variablen- oder Spaltennamen zuzuweisen, um sie weiterhin verwenden zu können:

# convert Series
my_series = pd.to_numeric(my_series)

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

Sie können es auch verwenden, um mehrere Spalten eines DataFrame über die folgende apply()Methode zu konvertieren :

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

Solange Ihre Werte alle konvertiert werden können, ist das wahrscheinlich alles, was Sie brauchen.

Fehlerbehandlung

Was aber, wenn einige Werte nicht in einen numerischen Typ konvertiert werden können?

to_numeric()Außerdem wird ein errorsSchlüsselwortargument verwendet, mit dem Sie nicht numerische Werte erzwingen NaNoder Spalten mit diesen Werten einfach ignorieren können.

Hier ist ein Beispiel mit einer Reihe von Zeichenfolgen smit dem Objekt dtype:

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

Das Standardverhalten ist das Erhöhen, wenn ein Wert nicht konvertiert werden kann. In diesem Fall kann es mit der Zeichenfolge 'pandas' nicht umgehen:

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

Anstatt zu scheitern, möchten wir vielleicht, dass 'Pandas' als fehlender / schlechter numerischer Wert betrachtet werden. NaNMit dem errorsSchlüsselwortargument können wir ungültige Werte wie folgt erzwingen:

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

Die dritte Option für errorsbesteht darin, die Operation zu ignorieren, wenn ein ungültiger Wert festgestellt wird:

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

Diese letzte Option ist besonders nützlich, wenn Sie Ihren gesamten DataFrame konvertieren möchten, aber nicht wissen, welche unserer Spalten zuverlässig in einen numerischen Typ konvertiert werden können. In diesem Fall schreiben Sie einfach:

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

Die Funktion wird auf jede Spalte des DataFrame angewendet. Spalten, die in einen numerischen Typ konvertiert werden können, werden konvertiert, während Spalten, die dies nicht können (z. B. nichtstellige Zeichenfolgen oder Datumsangaben), in Ruhe gelassen werden.

Downcasting

Standardmäßig erhalten Sie bei der Konvertierung mit to_numeric()entweder einen int64oder einen d- float64Typ (oder eine beliebige ganzzahlige Breite, die für Ihre Plattform typisch ist).

Das ist normalerweise das, was Sie wollen, aber was ist, wenn Sie etwas Speicherplatz sparen und einen kompakteren D-Typ verwenden möchten, wie float32oder int8?

to_numeric()bietet Ihnen die Möglichkeit, entweder auf "Ganzzahl", "Vorzeichen", "Vorzeichen" oder "Float" herunterzuspielen. Hier ist ein Beispiel für eine einfache Reihe svon Ganzzahltypen:

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

Beim Downcasting auf 'Ganzzahl' wird die kleinstmögliche Ganzzahl verwendet, die die Werte enthalten kann:

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

Beim Downcasting auf "Float" wird in ähnlicher Weise ein kleinerer als normaler Floating-Typ ausgewählt:

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

2. astype()

Mit dieser astype()Methode können Sie explizit angeben, welchen D-Typ Ihr ​​DataFrame oder Ihre Serie haben soll. Es ist sehr vielseitig, da Sie versuchen können, von einem Typ zum anderen zu wechseln.

Grundlegende Verwendung

Wählen Sie einfach einen Typ aus: Sie können einen NumPy-Typ (z. B. np.int16), einige Python-Typen (z. B. bool) oder pandas-spezifische Typen (wie den kategorialen Typ) verwenden.

Rufen Sie die Methode für das Objekt auf, das Sie konvertieren möchten, und astype()versuchen Sie, sie für Sie zu konvertieren :

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

Beachten Sie, dass ich "try" gesagt habe. Wenn Sie astype()nicht wissen, wie ein Wert in der Serie oder im DataFrame konvertiert wird, wird ein Fehler ausgegeben. Wenn Sie beispielsweise einen NaNoder inf-Wert haben, wird beim Konvertieren in eine Ganzzahl eine Fehlermeldung angezeigt.

Ab Pandas 0.20.0 kann dieser Fehler durch Bestehen unterdrückt werden errors='ignore'. Ihr ursprüngliches Objekt wird unberührt zurückgegeben.

Achtung

astype()ist mächtig, konvertiert aber manchmal Werte "falsch". Zum Beispiel:

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

Dies sind kleine Ganzzahlen. Wie wäre es also mit einer Konvertierung in einen vorzeichenlosen 8-Bit-Typ, um Speicherplatz zu sparen?

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

Die Konvertierung funktionierte, aber die -7 wurde umwickelt, um 249 zu werden (dh 2 8 - 7)!

Der Versuch, pd.to_numeric(s, downcast='unsigned')stattdessen einen Downcast durchzuführen, kann diesen Fehler verhindern.


3. infer_objects()

In Version 0.21.0 von pandas wurde die Methode infer_objects()zum Konvertieren von Spalten eines DataFrame mit einem Objektdatentyp in einen spezifischeren Typ (Soft Conversions) eingeführt.

Hier ist beispielsweise ein DataFrame mit zwei Spalten des Objekttyps. Eine enthält tatsächliche Ganzzahlen und die andere enthält Zeichenfolgen, die Ganzzahlen darstellen:

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

Mit infer_objects()können Sie den Typ der Spalte 'a' in int64 ändern:

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

Die Spalte 'b' wurde in Ruhe gelassen, da ihre Werte Zeichenfolgen und keine Ganzzahlen waren. Wenn Sie versuchen möchten, die Konvertierung beider Spalten in einen Ganzzahltyp zu erzwingen, können Sie df.astype(int)stattdessen verwenden.

Alex Riley
quelle
8
Im Gegensatz zu .astype (float) werden dadurch Zeichenfolgen in NaNs konvertiert, anstatt einen Fehler auszulösen
Rob
11
.convert_objectsist entstellt seit 0.17- df.to_numericstattdessen verwenden
Matti Lyra
4
Danke - ich sollte diese Antwort aktualisieren. Es ist vielleicht erwähnenswert, dass pd.to_numericseine Begleitmethoden im Gegensatz zu jeweils nur einer Spalte funktionieren convert_objects. Die Diskussion über eine Ersatzfunktion in der API scheint noch nicht abgeschlossen zu sein . Ich hoffe, dass eine Methode, die über den gesamten DataFrame funktioniert, erhalten bleibt, da sie sehr nützlich ist.
Alex Riley
Wie konvertieren Sie am besten alle Spalten, die derzeit beispielsweise int64in sind int32?
RoyalTS
4
@ RoyalS: wahrscheinlich am besten zu verwenden astype(wie in der anderen Antwort), dh .astype(numpy.int32).
Alex Riley
447

Wie wäre es damit?

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
quelle
10
Ja! pd.DataFramehat ein dtypeArgument, mit dem Sie tun können, wonach Sie suchen. df = pd.DataFrame (a, Spalten = ['eins', 'zwei', 'drei'], dtype = float) In [2]: df.dtypes Out [2]: ein Objekt zwei float64 drei float64 dtype: Objekt
Hernamesbarbara
17
Wenn ich es wie vorgeschlagen versuche, erhalte ich eine Warnung 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. Dies wurde möglicherweise in einer neueren Version von Pandas eingeführt, und ich sehe darin nichts Falsches, aber ich frage mich nur, worum es bei dieser Warnung geht. Irgendeine Idee?
Orange
2
@orange Die Warnung besteht darin, Benutzer auf potenziell verwirrendes Verhalten bei verketteten Vorgängen und bei Pandas aufmerksam zu machen, die Kopien von Datenrahmen zurückgeben, anstatt diese zu bearbeiten. Siehe stackoverflow.com/questions/20625582/… und verwandte Themen .
A.Wan
19
Das ist eine gute Methode, funktioniert aber nicht, wenn eine Spalte NaN enthält. Habe keine Ahnung, warum NaN beim Casting von float auf int einfach nicht NaN bleiben kann:ValueError: Cannot convert NA to integer
Vitaly Isaev
7
@ GillBates ja, in einem Wörterbuch. df = pd.DataFrame(a, columns=['one', 'two', 'three'], dtype={'one': str, 'two': int, 'three': float}). Es fällt mir jedoch schwer, die Spezifikation für akzeptierte "dtype" -Werte zu finden. Eine Liste wäre schön (derzeit mache ich dict(enumerate(my_list))).
FichteFoll
39

Dieser folgende Code ändert den Datentyp der Spalte.

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

Anstelle des Datentyps können Sie Ihren Datentyp angeben. Was möchten Sie wie str, float, int usw.

Akash Nayak
quelle
Beachten Sie jedoch, dass beim Anwenden auf eine Spalte mit den Zeichenfolgen "True" und "False" unter Verwendung des Datentyps boolalles in geändert wird True.
H. Vabri
Diese Option können Sie auch in Typ "Kategorie" konvertieren
neves
17

Wenn ich nur bestimmte Spalten angeben musste und explizit sein möchte, habe ich Folgendes verwendet (gemäß DOCS LOCATION ):

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

Verwenden Sie also die ursprüngliche Frage, geben Sie jedoch Spaltennamen an ...

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
quelle
15

Hier ist eine Funktion, die als Argumente einen DataFrame und eine Liste von Spalten verwendet und alle Daten in den Spalten zu Zahlen zwingt.

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

Also, für Ihr Beispiel:

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
quelle
Was wäre, wenn Sie Spaltenindizes anstelle von Spaltennamen verwenden möchten?
Jvalenti
8

Wie wäre es, zwei Datenrahmen mit jeweils unterschiedlichen Datentypen für ihre Spalten zu erstellen und diese dann zusammenzufügen?

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

Ergebnisse

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

Nachdem der Datenrahmen erstellt wurde, können Sie ihn in der ersten Spalte mit Gleitkommavariablen und in der zweiten Spalte mit Zeichenfolgen (oder einem beliebigen Datentyp) füllen.

MikeyE
quelle
4

Pandas> = 1,0

Hier ist eine Tabelle, die einige der wichtigsten Konvertierungen in Pandas zusammenfasst.

Geben Sie hier die Bildbeschreibung ein

Konvertierungen in Zeichenfolgen sind trivial .astype(str)und in der Abbildung nicht dargestellt.

"Harte" versus "Weiche" Konvertierungen

Beachten Sie, dass "Konvertierungen" in diesem Zusammenhang entweder das Konvertieren von Textdaten in ihren tatsächlichen Datentyp (harte Konvertierung) oder das Ableiten geeigneterer Datentypen für Daten in Objektspalten (weiche Konvertierung) betreffen können. Schauen Sie sich das an, um den Unterschied zu veranschaulichen

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
quelle
1

Ich dachte, ich hätte das gleiche Problem, aber tatsächlich habe ich einen kleinen Unterschied, der die Lösung des Problems erleichtert. Für andere, die sich diese Frage ansehen, lohnt es sich, das Format Ihrer Eingabeliste zu überprüfen. In meinem Fall sind die Zahlen zunächst Floats und keine Strings wie in der Frage:

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

Wenn ich die Liste jedoch vor dem Erstellen des Datenrahmens zu stark verarbeite, verliere ich die Typen und alles wird zu einer Zeichenfolge.

Erstellen des Datenrahmens über ein Numpy-Array

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

gibt den gleichen Datenrahmen wie in der Frage an, wobei die Einträge in den Spalten 1 und 2 als Zeichenfolgen betrachtet werden. Jedoch tun

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

gibt tatsächlich einen Datenrahmen mit den Spalten im richtigen Format

SarahD
quelle
0

Ab Pandas 1.0.0 haben wir pandas.DataFrame.convert_dtypes. Sie können sogar steuern, welche Typen konvertiert werden sollen!

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
quelle