Attribuer des dtypes à la colonne de dataframe pandas

111

Je veux définir le dtypes de plusieurs colonnes dans pd.Dataframe(j'ai un fichier que j'ai dû analyser manuellement dans une liste de listes, car le fichier ne pouvait pas être utilisé pd.read_csv)

import pandas as pd
print pd.DataFrame([['a','1'],['b','2']],
                   dtype={'x':'object','y':'int'},
                   columns=['x','y'])

Je reçois

ValueError: entry not a 2- or 3- tuple

La seule façon de les définir est de parcourir chaque variable de colonne et de refondre avec astype.

dtypes = {'x':'object','y':'int'}
mydata = pd.DataFrame([['a','1'],['b','2']],
                      columns=['x','y'])
for c in mydata.columns:
    mydata[c] = mydata[c].astype(dtypes[c])
print mydata['y'].dtype   #=> int64

Y a-t-il un meilleur moyen?

Hatmatrix
la source
Ce serait peut-être une bonne demande de bogue / fonctionnalité , actuellement je ne suis pas sûr de ce que fait dtype arg (vous pouvez lui passer un scalaire, mais ce n'est pas strict) ...
Andy Hayden
2
FYI: df = pd.DataFrame([['a','1'],['b','2']], dtype='int', columns=['x','y'])"works" ... mais: s
Andy Hayden
1
Ouais, "fonctionne" en effet; imprévisible ...
hatmatrix
Ce problème GitHub pourrait bientôt devenir pertinent: github.com/pydata/pandas/issues/9287
Amelio Vazquez-Reina

Réponses:

65

Depuis la 0.17, vous devez utiliser les conversions explicites:

pd.to_datetime, pd.to_timedelta and pd.to_numeric

(Comme mentionné ci-dessous, plus de "magie", convert_objectsa été obsolète dans la version 0.17)

df = pd.DataFrame({'x': {0: 'a', 1: 'b'}, 'y': {0: '1', 1: '2'}, 'z': {0: '2018-05-01', 1: '2018-05-02'}})

df.dtypes

x    object
y    object
z    object
dtype: object

df

   x  y           z
0  a  1  2018-05-01
1  b  2  2018-05-02

Vous pouvez les appliquer à chaque colonne que vous souhaitez convertir:

df["y"] = pd.to_numeric(df["y"])
df["z"] = pd.to_datetime(df["z"])    
df

   x  y          z
0  a  1 2018-05-01
1  b  2 2018-05-02

df.dtypes

x            object
y             int64
z    datetime64[ns]
dtype: object

et confirmez que le dtype est mis à jour.


ANSWER / DEPRECATED ANSWER for pandas 0.12 - 0.16: Vous pouvez utiliser convert_objectspour déduire de meilleurs dtypes:

In [21]: df
Out[21]: 
   x  y
0  a  1
1  b  2

In [22]: df.dtypes
Out[22]: 
x    object
y    object
dtype: object

In [23]: df.convert_objects(convert_numeric=True)
Out[23]: 
   x  y
0  a  1
1  b  2

In [24]: df.convert_objects(convert_numeric=True).dtypes
Out[24]: 
x    object
y     int64
dtype: object

La magie! (Triste de le voir obsolète.)

Andy Hayden
la source
2
comme type.convertdans R un peu; sympa mais laisse dans certains cas un souhait de spécifications explicites.
hatmatrix
1
Soyez prudent si vous avez une colonne qui doit être une chaîne mais qui contient au moins une valeur qui pourrait être convertie en un entier. Tout ce qu'il faut, c'est une valeur et le champ entier est converti en float64
Michael David Watson
18
J'ai remarqué qu'il convert_objects()était obsolète ... je ne sais pas ce qui l'a remplacé?
joefromct
6
Pour ré-inférer les types de données pour les colonnes d'objets, utilisez DataFrame.infer_objects ()
James Tobin
1
@smci ok, j'ai édité. Il y a un tas de réponses obsolètes, je dois trouver un moyen de toutes les trouver.
Andy Hayden
62

Pour ceux venant de Google (etc.) comme moi:

convert_objects est obsolète depuis la version 0.17 - si vous l'utilisez, vous obtenez un avertissement comme celui-ci:

FutureWarning: convert_objects is deprecated.  Use the data-type specific converters 
pd.to_datetime, pd.to_timedelta and pd.to_numeric.

Vous devriez faire quelque chose comme ceci:

Jack Yates
la source
Si vous avez ajouté quelques exemples, pd.to_datetime, to_timedelta, to_numericcela devrait être la réponse acceptée.
smci
41

vous pouvez définir les types explicitement avec pandas DataFrame.astype(dtype, copy=True, raise_on_error=True, **kwargs)et passer dans un dictionnaire avec les dtypes que vous souhaitezdtype

voici un exemple:

import pandas as pd
wheel_number = 5
car_name = 'jeep'
minutes_spent = 4.5

# set the columns
data_columns = ['wheel_number', 'car_name', 'minutes_spent']

# create an empty dataframe
data_df = pd.DataFrame(columns = data_columns)
df_temp = pd.DataFrame([[wheel_number, car_name, minutes_spent]],columns = data_columns)
data_df = data_df.append(df_temp, ignore_index=True) 

In [11]: data_df.dtypes
Out[11]:
wheel_number     float64
car_name          object
minutes_spent    float64
dtype: object

data_df = data_df.astype(dtype= {"wheel_number":"int64",
        "car_name":"object","minutes_spent":"float64"})

maintenant tu peux voir que ça a changé

In [18]: data_df.dtypes
Out[18]:
wheel_number       int64
car_name          object
minutes_spent    float64
Lauren
la source
13

Une autre façon de définir les types de colonnes consiste à commencer par construire un tableau d'enregistrements numpy avec les types souhaités, à le remplir, puis à le transmettre à un constructeur DataFrame.

import pandas as pd
import numpy as np    

x = np.empty((10,), dtype=[('x', np.uint8), ('y', np.float64)])
df = pd.DataFrame(x)

df.dtypes ->

x      uint8
y    float64
Kaushik Ghose
la source
0

face à un problème similaire à vous. Dans mon cas, j'ai des milliers de fichiers à partir de journaux Cisco que je dois analyser manuellement.

Afin d'être flexible avec les champs et les types, j'ai testé avec succès en utilisant StringIO + read_cvs qui accepte en effet un dict pour la spécification dtype.

J'obtiens généralement chacun des fichiers (5k-20k lignes) dans un tampon et crée les dictionnaires dtype dynamiquement.

Finalement, je concatène (avec catégorique ... grâce à 0.19) ces dataframes dans une grande trame de données que je vide dans hdf5.

Quelque chose dans ce sens

import pandas as pd
import io 

output = io.StringIO()
output.write('A,1,20,31\n')
output.write('B,2,21,32\n')
output.write('C,3,22,33\n')
output.write('D,4,23,34\n')

output.seek(0)


df=pd.read_csv(output, header=None,
        names=["A","B","C","D"],
        dtype={"A":"category","B":"float32","C":"int32","D":"float64"},
        sep=","
       )

df.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 4 columns):
A    5 non-null category
B    5 non-null float32
C    5 non-null int32
D    5 non-null float64
dtypes: category(1), float32(1), float64(1), int32(1)
memory usage: 205.0 bytes
None

Pas très pythonique ... mais fait le boulot

J'espère que ça aide.

JC

Julien C
la source
0

Il vaut mieux utiliser np.arrays typé, puis transmettre les données et les noms de colonne sous forme de dictionnaire.

import numpy as np
import pandas as pd
# Feature: np arrays are 1: efficient, 2: can be pre-sized
x = np.array(['a', 'b'], dtype=object)
y = np.array([ 1 ,  2 ], dtype=np.int32)
df = pd.DataFrame({
   'x' : x,    # Feature: column name is near data array
   'y' : y,
   }
 )
Clem Wang
la source