web-dev-qa-db-fra.com

Changer le type de données des colonnes dans les pandas

Je veux convertir une table, représentée sous forme de liste de listes, en un Pandas DataFrame. A titre d'exemple extrêmement simplifié:

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

Quel est le meilleur moyen de convertir les colonnes en types appropriés, dans ce cas les colonnes 2 et 3 en floats? Existe-t-il un moyen de spécifier les types lors de la conversion en DataFrame? Ou est-il préférable de créer d'abord le DataFrame, puis de parcourir les colonnes pour changer le type de chaque colonne? Idéalement, j'aimerais procéder de manière dynamique car il peut y avoir des centaines de colonnes et je ne veux pas spécifier exactement quelles colonnes sont de quel type. Tout ce que je peux garantir, c'est que chaque colonne contient des valeurs du même type.

507
user1642513

Vous avez trois options principales pour convertir des types en pandas.


1. to_numeric()

Le meilleur moyen de convertir une ou plusieurs colonnes d'un DataFrame en valeurs numériques consiste à utiliser pandas.to_numeric() .

Cette fonction essaiera de changer les objets non numériques (tels que les chaînes) en nombres entiers ou en nombres à virgule flottante, selon le cas.

Utilisation de base

L'entrée dans to_numeric() est une série ou une colonne unique d'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

Comme vous pouvez le constater, une nouvelle série est renvoyée. N'oubliez pas d'affecter cette sortie à un nom de variable ou de colonne pour continuer à l'utiliser:

# convert Series
my_series = pd.to_numeric(my_series)

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

Vous pouvez également l'utiliser pour convertir plusieurs colonnes d'un DataFrame via la méthode 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)

Tant que vos valeurs peuvent toutes être converties, c'est probablement tout ce dont vous avez besoin.

La gestion des erreurs

Mais que se passe-t-il si certaines valeurs ne peuvent pas être converties en un type numérique?

to_numeric() utilise également un argument de mot clé errors qui vous permet de forcer les valeurs non numériques à être NaN ou simplement d’ignorer les colonnes contenant ces valeurs.

Voici un exemple utilisant une série de chaînes s dont l'objet est dtype:

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

Le comportement par défaut est d'élever s'il ne peut pas convertir une valeur. Dans ce cas, il ne peut pas faire face à la chaîne 'pandas':

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

Plutôt que d’échouer, nous voudrions peut-être que les «pandas» soient considérés comme une valeur numérique manquante/mauvaise. Nous pouvons contraindre les valeurs non valides à NaN comme suit à l'aide de l'argument de mot clé errors:

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

La troisième option pour errors consiste simplement à ignorer l'opération si une valeur non valide est rencontrée:

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

Cette dernière option est particulièrement utile lorsque vous souhaitez convertir l’ensemble de votre DataFrame, sans savoir quelle colonne peut être convertie de manière fiable en un type numérique. Dans ce cas, écrivez simplement:

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

La fonction sera appliquée à chaque colonne du DataFrame. Les colonnes pouvant être converties en un type numérique seront converties, tandis que les colonnes qui ne le peuvent pas (par exemple, elles contiennent des chaînes ou des dates non numériques) seront laissées seules.

Downcasting

Par défaut, la conversion avec to_numeric() vous donnera un type int64 ou float64 dactylographié (ou une largeur quelconque, quelle que soit la largeur de l’entier, sur votre plate-forme).

C’est généralement ce que vous voulez, mais si vous vouliez économiser de la mémoire et utiliser un type de fichier plus compact, tel que float32 ou int8?

to_numeric() vous donne la possibilité de rétrograder en "entier", "signé", "non signé", "flottant". Voici un exemple pour une série simple s de type entier:

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

Le downcasting en 'entier' utilise le plus petit entier possible pouvant contenir les valeurs:

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

Le downcasting pour 'float' sélectionne de manière similaire un type flottant plus petit que la normale:

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

2. astype()

La méthode astype() vous permet d’être explicite sur le type de données que vous souhaitez attribuer à votre DataFrame ou à votre série. C'est très polyvalent en ce que vous pouvez essayer de passer d'un type à l'autre.

Utilisation de base

Il suffit de choisir un type: vous pouvez utiliser un type NumPy (par exemple, np.int16), certains types Python (par exemple, bool) ou des types spécifiques aux pandas (comme le type catégorique).

Appelez la méthode sur l'objet que vous voulez convertir et astype() essaiera de la convertir pour vous:

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

Remarquez que j'ai dit "essayer" - si astype() ne sait pas comment convertir une valeur dans Series ou DataFrame, une erreur sera générée. Par exemple, si vous avez une valeur NaN ou inf, vous obtiendrez une erreur en essayant de la convertir en entier.

A partir de pandas 0.20.0, cette erreur peut être supprimée en passant errors='ignore'. Votre objet d'origine sera retourné intact.

Faites attention

astype() est puissant, mais il convertit parfois les valeurs "de manière incorrecte". Par exemple:

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

Ce sont de petits entiers, alors pourquoi ne pas convertir en un type 8 bits non signé pour économiser de la mémoire?

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

La conversion a fonctionné, mais le -7 a été bouclé pour devenir 249 (c.-à-d. 28 - 7)! 

Essayer à la baisse d'utiliser pd.to_numeric(s, downcast='unsigned') pourrait aider à prévenir cette erreur.


3. infer_objects()

La version 0.21.0 de pandas a introduit la méthode infer_objects() pour convertir les colonnes d'un DataFrame ayant un type de données d'objet en un type plus spécifique (conversions logicielles).

Par exemple, voici un DataFrame avec deux colonnes de type d'objet. L'un contient les entiers réels et l'autre des chaînes représentant des entiers:

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

En utilisant infer_objects(), vous pouvez changer le type de colonne 'a' en int64:

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

La colonne 'b' a été laissée seule puisque ses valeurs étaient des chaînes, pas des entiers. Si vous voulez essayer de forcer la conversion des deux colonnes en un type entier, vous pouvez utiliser df.astype(int) à la place.

635
Alex Riley

Que dis-tu de ça? 

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
411
hernamesbarbara

ce code ci-dessous changera le type de données de la colonne.

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

à la place du type de données, vous pouvez indiquer votre type de données.

28
Akash Nayak

Voici une fonction qui prend comme arguments un DataFrame et une liste de colonnes et convertit toutes les données des colonnes en nombres.

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

Donc, pour votre exemple:

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'])
12
Harry Stevens

Que diriez-vous de créer deux cadres de données, chacun avec des types de données différents pour leurs colonnes, puis de les ajouter ensemble?

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

Résultats

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

Une fois le cadre de données créé, vous pouvez le renseigner avec des variables à virgule flottante dans la première colonne et des chaînes (ou tout type de données de votre choix) dans la deuxième colonne. 

6
MikeyE

Quand je n'ai eu besoin que de spécifier des colonnes spécifiques et que je veux être explicite, j'ai utilisé (par DOCS LOCATION ):

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

Donc, en utilisant la question originale, mais en lui donnant des noms de 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'})
2
Thom Ives

Je pensais avoir le même problème, mais en réalité, j'ai une légère différence qui le rend plus facile à résoudre. Pour les autres qui consultent cette question, il vaut la peine de vérifier le format de votre liste de saisie. Dans mon cas, les nombres sont initialement des flottants, pas des chaînes, comme dans la question:

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

mais en traitant trop la liste avant de créer la structure de données, je perds les types et tout devient une chaîne. 

Création du cadre de données via un tableau 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')

donne le même cadre de données que dans la question, où les entrées des colonnes 1 et 2 sont considérées comme des chaînes. Cependant faire

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

ne donne réellement un cadre de données avec les colonnes dans le format correct

0
SarahD