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

Posted

技术标签:

【中文标题】Python Pandas - 将某些列类型更改为类别【英文标题】:Python Pandas - Changing some column types to categories 【发布时间】:2015-05-08 18:29:08 【问题描述】:

我已将以下 CSV 文件输入 iPython Notebook:

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

我想将“公园”、“游乐场”、“运动”和“道路”更改为类别(它们中有李克特量表响应 - 但每列都有不同类型的李克特响应(例如,一个“非常同意” ,“同意”等,另一个有“非常重要”,“重要”等),其余为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

有没有办法将“公园”、“游乐场”、“运动”、“道路”更改为类别(然后可以分析李克特量表响应),留下“居民”和“儿童”(以及 94其他列是字符串,int + floats)请保持不变?或者,有没有更好的方法来做到这一点?如果有人有任何建议和/或反馈,我将不胜感激....我的头发慢慢秃了!

非常感谢。

已编辑添加 - 我使用的是 Python 2.7。

【问题讨论】:

【参考方案1】:

使用列表推导(避免循环),这会将所有具有 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')

这是我在堆栈上的第一个答案,所以请善待。

【讨论】:

【参考方案2】:

不需要循环,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

【讨论】:

【参考方案3】:

为了让事情变得更容易。不适用。没有地图。没有循环。

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

【讨论】:

这就是我想要的。谢谢!【参考方案4】:

Jupyter 笔记本

在我的例子中,我有很多我想将其转换为类别的对象的大数据框。

因此,我所做的是选择对象列并填充任何不适用的内容,然后将其保存在原始数据框中,如下所示

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

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

【讨论】:

【参考方案5】:

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

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

【讨论】:

【参考方案6】:

您可以使用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_dtypes(尽管在您的示例中,您似乎想要所有 object 类型)。

【讨论】:

优秀+1!真正的pythonic和简洁的解决方案。也很棒,因为它使用查询来获取所需的列,而不是将它们作为硬编码数组传递。 df = df.apply(lambda s: s.astype('category') if s.name in ['parks', 'playgrounds', 'sports'] else s) df[categoricals] = df[categoricals].apply(lambda x: x.astype('category'))【参考方案7】:

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

=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 notebook)。

# 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

【讨论】:

【参考方案8】:

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

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

【讨论】:

非常感谢@unutbu,这很有效。我不敢相信它是如此简单,我现在觉得自己很愚蠢! 如果我遇到错误怎么办? /Users/air/anaconda/lib/python2.7/site-packages/ipykernel/__main__.py:7:SettingWithCopyWarning:试图在数据帧的切片副本上设置值。尝试改用 .loc[row_indexer,col_indexer] = value 查看文档中的警告:pandas.pydata.org/pandas-docs/stable/… @JanSila:如果public 是另一个DataFrame 的子DataFrame 并且具有从另一个DataFrame复制的数据,您可能会得到UserWarning。出于谨慎考虑,Pandas 会发出 UserWarning 警告您修改 public 不会修改其他 DataFrame。如果修改其他 DataFrame 不是您打算做的或者不是问题,那么您可以随意忽略 UserWarning。如果您仍然想silence the UserWarning,请在进行public[col] = ... 形式的分配之前放置public.is_copy = False @unutbu 谢谢,我没有从文档中得到它。这很清楚。有点像 C++ 中的深拷贝和浅拷贝,对吧?我铸造了原始变量,然后复制。或者在制作子集时使用 .copy() 也可以,如果有人也面临这个问题。 @JanSila:是的,没错。 public.copy() 也可以,但请注意,如果 public 是一个大 DataFrame,public.copy() 可能比设置标志 public.is_copy = False 慢得多。 (此外,仅当 public 是副本时,UserWarning 才相关,因此具有讽刺意味的是,我们需要制作另一个副本只是为了使警告静音。)另一方面,我不认为 public.is_copy = False 是记录在案。我通过阅读源代码找到了它。因此,如果坚持使用记录在案的 API 是一个优先事项,您可能希望使用 public = public.copy()

以上是关于Python Pandas - 将某些列类型更改为类别的主要内容,如果未能解决你的问题,请参考以下文章

熊猫:将系列的数据类型更改为字符串

我有单元素数组。我如何将它们更改为元素本身?

将某些行更改为列

使用 MultiIndex 时,如何将此 Pandas 列类型保留为日期时间?

Python Pandas:如果数据为NaN,则更改为0,否则在数据框中更改为1

将现有 MySQL 列更改为 JSON 数据类型