济宁那家做网站最好,互联网推广服务,为什么原网站建设公司不愿意透露域名管理权限给客户,沧州网络公司引言
category是pandas的一种分类的定类数据类型。和文本数据.str.methond一样#xff0c;它也有访问器功能.cat.method。
本文将介绍#xff1a; 什么是分类数据#xff1f; 分类数据cat的处理方法 为什么要使用分类数据#xff1f; 分类数据cat使用…引言
category是pandas的一种分类的定类数据类型。和文本数据.str.methond一样它也有访问器功能.cat.method。
本文将介绍 什么是分类数据 分类数据cat的处理方法 为什么要使用分类数据 分类数据cat使用时的一些坑
什么是分类数据
分类数据表达数值具有某种属性、类型和特征也是我们理解的定类数据。比如人口按性别分为男和女按年龄分为老、中、少。
在计算机语言里我们通常会用数字来表示比如用1代表男0代表女但是0和1之间并没有大小关系pandas中用category来表示分类数据。
创建分类数据
创建数据时可以用dtpye来指定类型比如
s pd.Series([a,b,c],dtypecategory)
s
------
0 a
1 b
2 c
dtype: category
Categories (3, object): [a, b, c]
自动创建分类数据
在某些操作情况下会自动转变为分类类型比如用cut进行分箱操作返回的分箱就是分类类型。
pd.Series(pd.cut(range(1,10,2),3))
-----------------
0 (0.992, 3.667]
1 (0.992, 3.667]
2 (3.667, 6.333]
3 (6.333, 9.0]
4 (6.333, 9.0]
dtype: category
Categories (3, interval[float64]): [(0.992, 3.667] (3.667, 6.333] (6.333, 9.0]]
分类数据类型转换
直接用astype方法转换即可如
s pd.Series([a,b,c])
s
------
0 a
1 b
2 c
dtype: objects.astype(category)
------
0 a
1 b
2 c
dtype: category
Categories (3, object): [a, b, c]
自定义分类数据
除此之外还可以通过CategoricalDtype自定义分类数据自定义的类型适用于以上全部方法。
比如下面自定义了abc3个分类并指定了顺序。然后就可以通过dtype指定自定义的数据类型了d不在定义类型abc中显示为空。
from pandas.api.types import CategoricalDtype
# 自定义分类数据有序
c CategoricalDtype(categories[a,b,c],orderedTrue)
pd.Series(list(abcabd),dtypec)
--------
0 a
1 b
2 c
3 a
4 b
5 NaN
dtype: category
Categories (3, object): [a b c]
分类数据的处理方法
修改分类
通过.cat.rename_categories()修改分类的名称。
s pd.Series([a,b,c],dtypecategory)
# 指定分类为x、y、z
s.cat.categories [x,y,z]
0 x
1 y
2 z
dtype: category
Categories (3, object): [x, y, z]# 列表形式修改分类类型为mno
s.cat.rename_categories([m,n,o])
# 字典形式
s.cat.rename_categories({x:m,y:n,z:o})
0 m
1 n
2 o
dtype: category
Categories (3, object): [m, n, o]
追加新分类
通过.cat.add_categories()追加分类。
s.cat.add_categories([r,t])
0 x
1 y
2 z
dtype: category
Categories (5, object): [x, y, z, r, t]
删除分类
同理也可以删除分类。有两种方法remove_categories和remove_unused_categories。
# 删除指定的分类r和t
s.cat.remove_categories([r,t])
# 自动删除未使用的分类
s.cat.remove_unused_categories()
顺序
默认情况下分类数据不自动排序可以通过前面CategoricalDtype设置顺序或者通过.cat.as_ordered设置。
# 有序设置
s.cat.as_ordered()
0 x
1 y
2 z
dtype: category
Categories (3, object): [x y z]
# 无序设置
s.cat.as_unordered()
# 重新排序
s.cat.reorder_categories([y,x,z], orderedTrue)
为什么使用category数据类型
总结一下使用category有以下一些好处 内存使用情况:对于重复值很多的字符串列category可以大大减少将数据存储在内存中所需的内存量; 运行性能:进行了一些优化可以提高某些操作的执行速度 算法库的适用在某些情况下一些算法模型需要category这种类型。比如我们知道lightgbm相对于xgboost优化的一个点就是可以处理分类变量而在构建模型时我们需要指定哪些列是分类变量并将它们调整为category作为超参数传给模型。
一个简单的例子。
df_size 100_000
df1 pd.DataFrame({float_1: np.random.rand(df_size),species: np.random.choice([cat, dog, ape, gorilla], sizedf_size),}
)
df1_cat df1.astype({species: category})
创建了两个DataFrame其中df1包含了species并且为object类型df1_cat复制了df1但指定了species为category类型。 df1.memory_usage(deepTrue)
Index 128
float_1 800000
species 6100448
dtype: int64
就内存使用而言我们可以直接看到包含字符串的列的成本是多高。species列的字符串大约占用了6MB如果这些字符串较长则将会更多。 df1_cat.memory_usage(deepTrue)
Index 128
float_1 800000
species 100416
dtype: int64
再看转换为category类别后的内存使用情况。有了相当大的改进使用的内存减少了大约60倍。没有对比就没有伤害。
这就是使用category的其中一个好处。
使用category的一些坑
但爱之深责之切呀category有很多坑要注意这里东哥总结出以下几点供大家参考。
1、category列的操作
好吧这部分应该才是大家较为关心的因为经常会遇到一些莫名其妙的报错或者感觉哪里不对又不知道问题出在哪里。
首先说明一下使用category的时候需要格外小心因为如果姿势不对它就很可能变回object 。而变回object的结果就是会降低代码的性能因为强制转换类型成本很高并会消耗内存。
日常面对category类型的数据我们肯定是要对其进行操作的比如做一些转换。下面看一个例子我们要分别对category和object类型进行同样的字符串大写操作使用accessor的.str方法。
在非category字符串上 %timeit df1[species].str.upper()
25.6 ms ± 2.07 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
在category字符串上 %timeit df1_cat[species].str.upper()
1.85 ms ± 41.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
结果很明显了。在这种情况下速度提高了大约14倍因为内部优化会让.str.upper()仅对分类的唯一类别值调用一次然后根据结果构造一个seires而不是对结果中的每个值都去调用一次。
怎么理解假设现有一个列叫animal其类别有cat和dog两种假设样本为10000个4000个cat和6000个dog。那么如果我用对category本身处理意味着我只分别对cat和dog两种类别处理一次一共两次就解决。如果对每个值处理那就需要样本数量10000次的处理。
尽管从时间上有了一些优化然而这种方法的使用也是有一些问题的。。。看一下内存使用情况。 df1_cat[species].str.upper().memory_usage(deepTrue)
6100576
意外的发现category类型丢了。。结果竟是一个object类型数据压缩的效果也没了现在的结果再次回到刚才的6MB内存占用。
这是因为使用str会直接让原本的category类型强制转换为object所以内存占用又回去了这是我为什么最开始说要格外小心。
解决方法就是直接对category本身操作而不是对它的值操作。 要直接使用cat的方法来完成转换操作如下。
%timeit df1_cat[species].cat.rename_categories(str.upper)
239 µs ± 13.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
可以看到这个速度就更快了因为省去了将category类别转换为object的时间并且内存占用也非常少。因此这才是最优的做法。
2、与category列的合并
还是上面那个例子但是这次增加了habitat一列并且species中增加了sanke。
df2 pd.DataFrame({species: [cat, dog, ape, gorilla, snake],habitat: [house, house, jungle, jungle, jungle],}
)
df2_cat df2.astype({species: category, habitat: category})
和前面一样创建该数据集的一个category版本并创建了一个带有object字符串的版本。如果将两个object列合并在一起的没什么意思因为大家都知道会发生什么object object object而已。
把object列合并到category列上
接着上面的例子。 df1.merge(df2_cat, onspecies).dtypes
float_1 float64
species object
habitat category
dtype: object
左边的df1中species列为object,右边的df2_cat中species列为category。我们可以看到当我们合并时在结果中的合并列会得到category object object。
这显然不行了又回到原来那样了。我们再试下其他情况。
两个category列的合并 df1_cat.merge(df2_cat, onspecies).dtypes
float_1 float64
species object
habitat category
dtype: object
结果是category category object
有点想打人了但是别急我们看看为啥。
在合并中为了保存分类类型两个category类型必须是完全相同的。 这个与pandas中的其他数据类型略有不同例如所有float64列都具有相同的数据类型就没有什么区分。
而当我们讨论category数据类型时该数据类型实际上是由该特定类别中存在的一组值来描述的因此一个类别包含[cat, dog, mouse]与类别包含[cheese, milk, eggs]是不一样的。上面的例子之所以没成功是因为多加了一个snake。
因此我们可以得出结论 category1 category2object category1 category1category1
因此解决办法就是两个category类别一模一样让其中一个等于另外一个。 df1_cat.astype({species: df2_cat[species].dtype}).merge(df2_cat, onspecies).dtypesfloat_1 float64
species category
habitat category
dtype: object
3、category列的分组
用category类列分组时一旦误操作就会发生意外结果是Dataframe会被填成空值还有可能直接跑死。。
当对category列分组时默认情况下即使category类别的各个类不存在值也会对每个类进行分组。
一个例子来说明。
habitat_df (df1_cat.astype({species: df2_cat[species].dtype}).merge(df2_cat, onspecies)
)
house_animals_df habitat_df.loc[habitat_df[habitat] house]
这里采用habitat_df从上面例子得到的筛选habitat为house的只有dog和cat是house看下面分组结果。 house_animals_df.groupby(species)[float_1].mean()
species
ape NaN
cat 0.501507
dog 0.501023
gorilla NaN
snake NaN
Name: float_1, dtype: float64
在groupby中得到了一堆空值。默认情况下当按category列分组时即使数据不存在pandas也会为该类别中的每个值返回结果。略坑如果数据类型包含很多不存在的尤其是在多个不同的category列上进行分组将会极其损害性能。
因此解决办法是可以传递observedTrue到groupby调用中这确保了我们仅获取数据中有值的组。 house_animals_df.groupby(species, observedTrue)[float_1].mean()
species
cat 0.501507
dog 0.501023
Name: float_1, dtype: float64
4、category列的索引
仍以上面例子举例使用groupby-unstack实现了一个交叉表species作为列habitat作为行均为category类型。 species_df habitat_df.groupby([habitat, species], observedTrue)[float_1].mean().unstack()species_dfspecies cat ape dog gorilla
habitat
house 0.501507 NaN 0.501023 NaN
jungle NaN 0.501284 NaN 0.501108
这好像看似也没什么毛病我们继续往下看。为这个交叉表添加一个新列new_col值为1。 species_df[new_col] 1
TypeError: fill_valuenew_col is not present in this Categoricals categories
正常情况下上面这段代码是完全可以的但这里报错了为什么
原因是species和habitat现在均为category类型。使用.unstack()会把species索引移到列索引中类似pivot交叉表的操作。而当添加的新列不在species的分类索引中时就会报错。
总结一下pandas的category类型非常有用可以带来一些良好的性能优势。但是它也很娇气使用过程中要尤为小心确保category类型在整个流程中保持不变避免变回object。本文介绍的4个点注意点 category列的变换操作:直接对category本身操作而不是对它的值操作。这样可以保留分类性质并提高性能。 category列的合并合并时注意要保留category类型且每个dataframe的合并列中的分类类型必须完全匹配。 category列的分组:默认情况下获得数据类型中每个值的结果即使数据中不存在该结果。可以通过设置observedTrue调整。 category列的索引当索引为category类型的时候注意是否可能与类别变量发生奇怪的交互作用。