web-dev-qa-db-fra.com

Pandas Python - Modification de certains types de colonnes en catégories

J'ai introduit le fichier CSV suivant dans iPython Notebook:

public = pd.read_csv("categories.csv")
public

J'ai aussi importé des pandas en tant que pd, numpy en tant que np et matplotlib.pyplot en tant que plt. Les types de données suivants sont présents (le ci-dessous est un résumé - il y a environ 100 colonnes)

In [36]:   public.dtypes
Out[37]:   parks          object
           playgrounds    object
           sports         object
           roading        object               
           resident       int64
           children       int64

Je veux changer les catégories "parcs", "terrains de jeu", "sports" et "parcours" en catégories (elles ont des réponses à l'échelle de likert - chaque colonne contient différents types de réponses de type likert (par exemple, l'une est "tout à fait d'accord", "d'accord "etc., un autre a" très important "," important "etc.), le reste étant défini sur int64. 

J'ai pu créer une image distincte - public1 - et modifier l'une des colonnes en un type de catégorie à l'aide du code suivant:

public1 = {'parks': public.parks}
public1 = public1['parks'].astype('category')

Cependant, lorsque j'ai essayé de changer un numéro à la fois en utilisant ce code, j'ai échoué:

public1 = {'parks': public.parks,
           'playgrounds': public.parks}
public1 = public1['parks', 'playgrounds'].astype('category')

Malgré cela, je ne souhaite pas créer de cadre de données distinct avec uniquement les colonnes de catégories. Je voudrais qu'ils soient modifiés dans le cadre de données d'origine.

J'ai essayé de nombreuses façons pour y parvenir, puis le code ici: Pandas: changer le type de données des colonnes ...

public[['parks', 'playgrounds', 'sports', 'roading']] = public[['parks', 'playgrounds', 'sports', 'roading']].astype('category')

et a obtenu l'erreur suivante:

 NotImplementedError: > 1 ndim Categorical are not supported at this time

Existe-t-il un moyen de changer les termes "parcs", "terrains de jeux", "sports", "parcours" en catégories (afin d'analyser les réponses à l'échelle de likert), en laissant "résidents" et "enfants" (et les 94 autres colonnes sont string, int + floats) intactes s'il vous plaît? Ou, y a-t-il une meilleure façon de faire cela? Si quelqu'un a des suggestions et/ou des commentaires, je vous en serais très reconnaissant ... je vais lentement devenir chauve en train de me déchirer les cheveux!

Merci d'avance.

édité pour ajouter - J'utilise Python 2.7.

28
gincard

Parfois, il suffit d'utiliser une boucle for:

for col in ['parks', 'playgrounds', 'sports', 'roading']:
    public[col] = public[col].astype('category')
53
unutbu

Vous pouvez utiliser la méthode pandas.DataFrame.apply avec une expression lambda pour résoudre ce problème. Dans votre exemple, vous pouvez utiliser 

df[['parks', 'playgrounds', 'sports']].apply(lambda x: x.astype('category'))

Je ne connais pas de moyen d'exécuter ceci in-situ, donc je vais finir par avoir quelque chose comme ceci:

df[df.select_dtypes(['object']).columns] = df.select_dtypes(['object']).apply(lambda x: x.astype('category'))

Évidemment, vous pouvez remplacer .select_dtypes par des noms de colonnes explicites si vous ne souhaitez pas sélectionner tous les types de données (bien que, dans votre exemple, il semble que vous vouliez tous les types object).

30
Derek Kaknes

Depuis pandas 0.19.0, What's New indique que read_csv prend en charge l’analyse directe des colonnes Categorical directement . Cette réponse ne s’applique que si vous démarrez à partir de read_csv sinon, je pense que la réponse de unutbu est toujours préférable ... sur 10 000 enregistrements:

import pandas as pd
import numpy as np

# Generate random data, four category-like columns, two int columns
N=10000
categories = pd.DataFrame({
            'parks' : np.random.choice(['strongly agree','agree', 'disagree'], size=N),
            'playgrounds' : np.random.choice(['strongly agree','agree', 'disagree'], size=N),
            'sports' : np.random.choice(['important', 'very important', 'not important'], size=N),
            'roading' : np.random.choice(['important', 'very important', 'not important'], size=N),
            'resident' : np.random.choice([1, 2, 3], size=N),
            'children' : np.random.choice([0, 1, 2, 3], size=N)
                       })
categories.to_csv('categories_large.csv', index=False)

<0.19.0 (ou> = 19.0 sans spécifier dtype)

pd.read_csv('categories_large.csv').dtypes # inspect default dtypes

children        int64
parks          object
playgrounds    object
resident        int64
roading        object
sports         object
dtype: object

> = 0.19.0

Pour une analyse dtypes mixte en tant que Categorical, vous pouvez appliquer un dictionnaire dtype={'colname' : 'category', ...} dans read_csv.

pd.read_csv('categories_large.csv', dtype={'parks': 'category',
                                           'playgrounds': 'category',
                                           'sports': 'category',
                                           'roading': 'category'}).dtypes
children          int64
parks          category
playgrounds    category
resident          int64
roading        category
sports         category
dtype: object

Performance

Une légère accélération (carnet de notes local Jupyter), comme mentionné dans les notes de publication.

# unutbu's answer
%%timeit
public = pd.read_csv('categories_large.csv')
for col in ['parks', 'playgrounds', 'sports', 'roading']:
    public[col] = public[col].astype('category')
10 loops, best of 3: 20.1 ms per loop

# parsed during read_csv
%%timeit
category_cols = {item: 'category' for item in ['parks', 'playgrounds', 'sports', 'roading']}
public = pd.read_csv('categories_large.csv', dtype=category_cols)
100 loops, best of 3: 14.3 ms per loop
9
Kevin

J'ai trouvé que l'utilisation d'une boucle for fonctionne bien.

for col in ['col_variable_name_1', 'col_variable_name_2', ect..]:
    dataframe_name[col] = dataframe_name[col].astype(float)
0
NickTumi