Modifica il tipo di dati delle colonne in Panda


806

Voglio convertire una tabella, rappresentata come un elenco di elenchi, in a Pandas DataFrame. Come esempio estremamente semplificato:

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

Qual è il modo migliore per convertire le colonne nei tipi appropriati, in questo caso le colonne 2 e 3 in float? C'è un modo per specificare i tipi durante la conversione in DataFrame? O è meglio creare prima il DataFrame e poi scorrere le colonne per cambiare il tipo per ogni colonna? Idealmente, vorrei farlo in modo dinamico perché ci possono essere centinaia di colonne e non voglio specificare esattamente quali colonne sono di quale tipo. Tutto quello che posso garantire è che ogni colonna contiene valori dello stesso tipo.


Ho visto approcci per convertire ogni colonna e approcci per convertire colonne specificatamente nominate, ma che ne dici di determinate colonne che soddisfano una determinata condizione quando non puoi elencare 100 colonne che vuoi convertire contemporaneamente? Sto pensando ad esempio a tutte le tattiche float64 -> float32 o ad altre memorie salvavita.
demongolem il

@demongolem: potresti fare qualcosa di simile df.apply(pd.to_numeric, downcast="integer", errors="ignore")per eseguire il downgrade delle colonne di interi al tipo più piccolo (intero) che conterrà i valori.
Alex Riley,

Risposte:


1193

Hai tre opzioni principali per convertire i tipi in panda:

  1. to_numeric()- fornisce funzionalità per convertire in modo sicuro tipi non numerici (ad esempio stringhe) in un tipo numerico adatto. (Vedi anche to_datetime()e to_timedelta().)

  2. astype()- convertire (quasi) qualsiasi tipo in (quasi) qualsiasi altro tipo (anche se non è necessariamente sensato farlo). Ti consente anche di convertire in tipi categoriali (molto utile).

  3. infer_objects() - un metodo di utilità per convertire colonne di oggetti che contengono oggetti Python in un tipo di panda, se possibile.

Continua a leggere per spiegazioni più dettagliate e l'utilizzo di ciascuno di questi metodi.


1. to_numeric()

Il modo migliore per convertire una o più colonne di un DataFrame in valori numerici è utilizzare pandas.to_numeric().

Questa funzione tenterà di modificare gli oggetti non numerici (come le stringhe) in numeri interi o numeri in virgola mobile, a seconda dei casi.

Utilizzo di base

L'input per to_numeric()è una serie o una singola colonna di un 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

Come puoi vedere, viene restituita una nuova serie. Ricorda di assegnare questo output a un nome di variabile o colonna per continuare a usarlo:

# convert Series
my_series = pd.to_numeric(my_series)

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

Puoi anche usarlo per convertire più colonne di un DataFrame tramite il apply()metodo:

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

Finché i tuoi valori possono essere tutti convertiti, probabilmente è tutto ciò di cui hai bisogno.

Gestione degli errori

Ma cosa succede se alcuni valori non possono essere convertiti in un tipo numerico?

to_numeric()accetta anche un errorsargomento di parole chiave che consente di forzare valori non numerici NaNo semplicemente ignorare le colonne che contengono questi valori.

Ecco un esempio usando una serie di stringhe sche ha il tipo di oggetto:

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

Il comportamento predefinito è aumentare se non è possibile convertire un valore. In questo caso, non può far fronte alla stringa "panda":

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

Piuttosto che fallire, potremmo volere che i "panda" siano considerati un valore numerico mancante / cattivo. Possiamo forzare i valori non validi NaNcome segue usando l' errorsargomento keyword:

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

La terza opzione per errorsè semplicemente ignorare l'operazione se viene rilevato un valore non valido:

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

Quest'ultima opzione è particolarmente utile quando si desidera convertire l'intero DataFrame, ma non si sa quale delle nostre colonne può essere convertita in modo affidabile in un tipo numerico. In tal caso basta scrivere:

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

La funzione verrà applicata a ciascuna colonna di DataFrame. Le colonne che possono essere convertite in un tipo numerico verranno convertite, mentre le colonne che non possono (ad esempio contengono stringhe o cifre non numeriche) verranno lasciate sole.

downcasting

Per impostazione predefinita, la conversione con to_numeric()ti darà un int64o un float64tipo (o qualsiasi larghezza intera sia nativa per la tua piattaforma).

Di solito è quello che vuoi, ma se volessi risparmiare un po 'di memoria e utilizzare un dtype più compatto, come float32, o int8?

to_numeric()ti dà la possibilità di effettuare il downcast su 'intero', 'firmato', 'non firmato', 'float'. Ecco un esempio per una semplice serie sdi tipo intero:

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

Il downcasting su "intero" utilizza il numero intero più piccolo possibile che può contenere i valori:

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

Il downcasting su "float" seleziona allo stesso modo un tipo mobile più piccolo del normale:

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

2. astype()

Il astype()metodo consente di essere espliciti sul tipo che si desidera avere DataFrame o Series. È molto versatile in quanto puoi provare a passare da un tipo all'altro.

Utilizzo di base

Basta scegliere un tipo: è possibile utilizzare un tipo NumPy (ad esempio np.int16), alcuni tipi di Python (ad esempio bool) o tipi specifici di panda (come il tipo di categoria).

Chiama il metodo sull'oggetto che desideri convertire e astype()proverà a convertirlo per te:

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

Nota che ho detto "prova" - se astype()non sa come convertire un valore in Series o DataFrame, genererà un errore. Ad esempio, se hai un valore NaNo inf, visualizzerai un errore nel tentativo di convertirlo in un numero intero.

A partire da Panda 0.20.0, questo errore può essere eliminato passando errors='ignore'. Il tuo oggetto originale verrà restituito intatto.

Stai attento

astype()è potente, ma a volte converte i valori "in modo errato". Per esempio:

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

Questi sono numeri interi piccoli, quindi che ne dite di convertire in un tipo a 8 bit senza segno per risparmiare memoria?

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

La conversione ha funzionato, ma il -7 è stato concluso per diventare 249 (ovvero 2 8 - 7)!

Cercare di eseguire il downcast usando pd.to_numeric(s, downcast='unsigned')invece potrebbe aiutare a prevenire questo errore.


3. infer_objects()

La versione 0.21.0 di Panda ha introdotto il metodo infer_objects()per convertire le colonne di un DataFrame che ha un tipo di dati oggetto in un tipo più specifico (conversioni soft).

Ad esempio, ecco un DataFrame con due colonne di tipo oggetto. Uno contiene numeri interi reali e l'altro contiene stringhe che rappresentano numeri interi:

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

Usando infer_objects(), puoi cambiare il tipo di colonna 'a' in int64:

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

La colonna 'b' è stata lasciata sola poiché i suoi valori erano stringhe, non numeri interi. Se si desidera provare a forzare la conversione di entrambe le colonne in un tipo intero, è possibile utilizzare df.astype(int)invece.


8
Inoltre, a differenza di .astype (float), questo convertirà le stringhe in NaN invece di generare un errore
Rob

11
.convert_objectsè depracato poiché 0.17- usa df.to_numericinvece
Matti Lyra il

4
Grazie - Dovrei aggiornare questa risposta. Vale forse la pena notare che i pd.to_numericsuoi metodi associati funzioneranno solo su una colonna alla volta, a differenza convert_objects. La discussione su una funzione di sostituzione nell'API sembra essere in corso ; Spero che rimanga un metodo che funzioni su tutto il DataFrame perché è molto utile.
Alex Riley,

Qual è il modo migliore per convertire tutte le colonne che sono attualmente, diciamo, int64in int32?
RoyalTS,

4
@RoyalTS: probabilmente il migliore da usare astype(come nell'altra risposta), cioè .astype(numpy.int32).
Alex Riley,

447

Cosa ne pensi di questo?

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

10
Sì! pd.DataFrameha un dtypeargomento che potrebbe permetterti di fare ciò che stai cercando. df = pd.DataFrame (a, colonne = ['uno', 'due', 'tre'], dtype = float) In [2]: df.dtypes Out [2]: un oggetto due float64 tre float64 dtype: oggetto
hernamesbarbara,

17
Quando provo come suggerito, ricevo un avviso 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. Questo potrebbe essere stato introdotto in una nuova versione di Panda e di conseguenza non vedo nulla di sbagliato, ma mi chiedo solo di cosa tratta questo avviso. Qualche idea?
arancione

2
@arange l'avvertimento è di avvisare gli utenti di comportamenti potenzialmente confusi con operazioni concatenate e con i panda che restituiscono copie anziché modificare i frame di dati. vedi stackoverflow.com/questions/20625582/… e relativi.
A.

19
Questo è un buon metodo, ma non funziona quando ci sono NaN in una colonna. Non ho idea del perché NaN non riesca proprio a rimanere NaN quando lancia float su int:ValueError: Cannot convert NA to integer
Vitaly Isaev

7
@GillBates sì, in un dizionario. df = pd.DataFrame(a, columns=['one', 'two', 'three'], dtype={'one': str, 'two': int, 'three': float}). Tuttavia, faccio fatica a trovare le specifiche per i valori "dtype" accettati. Un elenco sarebbe bello (attualmente lo faccio dict(enumerate(my_list))).
FichteFoll,

39

questo codice qui sotto cambierà il tipo di dati della colonna.

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

al posto del tipo di dati puoi dare il tuo tipo di dati. cosa vuoi come str, float, int ecc.


Tieni presente che quando si applica questo su una colonna contenente le stringhe `` `` Vero '' `` e `` `` Falso '' `` usando il tipo di dati bool, tutto viene cambiato in True.
H. Vabri,

Questa opzione è anche possibile convertire per digitare "categoria"
neves

17

Quando ho solo bisogno di specificare colonne specifiche e voglio essere esplicito, ho usato (per LOCATION DOCUMENTI ):

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

Quindi, usando la domanda originale, ma fornendogli i nomi delle colonne ...

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

15

Ecco una funzione che prende come argomento un DataFrame e un elenco di colonne e costringe tutti i dati nelle colonne a numeri.

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

Quindi, per il tuo esempio:

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

cosa succede se si desidera utilizzare gli indici di colonna anziché i nomi di colonna?
jvalenti,

8

Che ne dici di creare due frame di dati, ognuno con tipi di dati diversi per le loro colonne e poi aggiungerli insieme?

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

risultati

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

Dopo aver creato il frame di dati, è possibile popolarlo con variabili a virgola mobile nella prima colonna e stringhe (o qualsiasi tipo di dati desiderato) nella seconda colonna.


4

panda> = 1.0

Ecco un grafico che riassume alcune delle conversioni più importanti nei panda.

inserisci qui la descrizione dell'immagine

Le conversioni in stringa sono banali .astype(str)e non sono mostrate nella figura.

Conversioni "difficili" contro "morbide"

Si noti che le "conversioni" in questo contesto potrebbero fare riferimento alla conversione dei dati di testo nel loro tipo di dati effettivo (conversione effettiva) o alla deduzione di tipi di dati più appropriati per i dati nelle colonne di oggetti (conversione soft). Per illustrare la differenza, dai un'occhiata

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                                                     

1

Pensavo di avere lo stesso problema, ma in realtà ho una leggera differenza che rende il problema più facile da risolvere. Per gli altri che guardano a questa domanda vale la pena controllare il formato del tuo elenco di input. Nel mio caso i numeri inizialmente sono float e non stringhe come nella domanda:

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

ma elaborando troppo l'elenco prima di creare il dataframe perdo i tipi e tutto diventa una stringa.

Creazione del frame di dati tramite una matrice 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')

fornisce lo stesso frame di dati della domanda, in cui le voci nelle colonne 1 e 2 sono considerate stringhe. Comunque lo sta facendo

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

in realtà fornisce un frame di dati con le colonne nel formato corretto


0

A partire da Panda 1.0.0, abbiamo pandas.DataFrame.convert_dtypes. Puoi persino controllare quali tipi convertire!

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.