Python Pandas - 将某些列类型更改为类别

Python Pandas - Changing some column types to categories

我已将以下 CSV 文件输入 iPython 笔记本:

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

我还导入了 pandas 作为 pd,numpy 作为 np 和 matplotlib.pyplot 作为 plt。存在以下数据类型(以下是摘要 - 大约有 100 列)

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

我想将 'parks'、'playgrounds'、'sports' 和 'roading' 更改为类别(它们中有李克特量表响应 - 每列都有不同类型的李克特尽管有响应(例如,一个有 "strongly agree"、"agree" 等,另一个有 "very important"、"important" 等),将余数保留为 int64。

我能够创建一个单独的数据框 - public1 - 并使用以下代码将其中一列更改为类别类型:

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

但是,当我尝试使用此代码一次更改号码时,我没有成功:

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

尽管如此,我不想创建仅包含类别列的单独数据框。我希望在原始数据框中更改它们。

我尝试了很多方法来实现这一点,然后尝试了这里的代码:Pandas: change data type of columns...

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

并出现以下错误:

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

有没有办法将 'parks'、'playgrounds'、'sports'、'roading' 更改为类别(这样就可以分析李克特量表的响应),留下 'resident' 和 'children' (以及其他 94 个字符串、int + 浮点数的列)请保持不变?或者,有更好的方法吗?如果有人有任何建议 and/or 反馈,我将不胜感激....我正在慢慢秃头,扯掉我的头发!

非常感谢。

编辑添加 - 我正在使用 Python 2.7.

有时,您只需要使用 for 循环:

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

从pandas 0.19.0开始,What's New说明read_csv支持直接解析Categorical列。 此答案仅在您从 read_csv 开始时适用,否则,我认为 unutbu 的答案仍然是最好的。 10,000 条记录的示例:

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(或 >=19.0 不指定 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

对于混合 dtypes 解析为 Categorical 可以通过在 read_csv.

中传递字典 dtype={'colname' : 'category', ...} 来实现
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

性能

轻微的加速(本地 jupyter 笔记本),如发行说明中所述。

# 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

您可以使用 pandas.DataFrame.apply 方法和 lambda 表达式来解决这个问题。在您的示例中,您可以使用

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

我不知道有什么方法可以就地执行此操作,所以通常我会得到这样的结果:

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

显然,如果您不想 select 某种数据类型的所有内容,您可以将 .select_dtypes 替换为明确的列名(尽管在您的示例中,您似乎想要所有 object 类型)。

我发现使用 for 循环效果很好。

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

Jupyter 笔记本

就我而言,我有一个包含许多对象的大 Dataframe,我想将其转换为类别。

因此,我所做的是选择对象列并填充任何缺失的 NA,然后将其保存在原始 Dataframe 中,如

# Convert Object Columns to Categories
obj_df =df.select_dtypes(include=['object']).copy()
obj_df=obj_df.fillna('Missing')
for col in obj_df:
    obj_df[col] = obj_df[col].astype('category')
df[obj_df.columns]=obj_df[obj_df.columns]
df.head()

我希望这可能是一个有用的资源供以后参考

不需要循环,Pandas现在可以直接做,只需要传递一个你想转换的列的列表,Pandas就会把它们全部转换。

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

df = pd.DataFrame({'a': ['a', 'b', 'c'], 'b': ['c', 'd', 'e']})

>>     a  b
>>  0  a  c
>>  1  b  d
>>  2  c  e

df.dtypes
>> a    object
>> b    object
>> dtype: object

df[df.columns] = df[df.columns].astype('category')
df.dtypes
>> a    category
>> b    category
>> dtype: object

让事情变得更简单。不申请。没有地图。没有循环。

cols=data.select_dtypes(exclude='int').columns.to_list()
data[cols]=data[cols].astype('category')

使用列表理解(避免循环),这会将所有 dtypes=object 的列转换为 dtypes=category。我已将 'df' 作为更通用的数据框。

df[[col for col in df.columns if df[col].dtypes == object]].astype('category', copy=False)

如果您出于某种原因想要避免“copy=False”参数(因为 python 文档告诉我们在使用它时要小心),您可以使用以下行。

df[[col for col in df.columns if df[col].dtypes == object]] = df[[col for col in df.columns if df[col].dtypes == object]].astype('category')

这是我在堆栈上的第一个答案,所以请多多关照。