网站开发和设计区别,wordpress文章发布没有页面模板,淘宝cms建站,深圳营销型网站原文#xff1a;pandas.pydata.org/docs/ 重复标签 原文#xff1a;pandas.pydata.org/docs/user_guide/duplicates.html Index对象不需要是唯一的#xff1b;你可以有重复的行或列标签。这一点可能一开始会有点困惑。如果你熟悉 SQL#xff0c;你会知道行标签类似于表上的… 原文pandas.pydata.org/docs/ 重复标签 原文pandas.pydata.org/docs/user_guide/duplicates.html Index对象不需要是唯一的你可以有重复的行或列标签。这一点可能一开始会有点困惑。如果你熟悉 SQL你会知道行标签类似于表上的主键你绝不希望在 SQL 表中有重复项。但 pandas 的一个作用是在数据传输到某个下游系统之前清理混乱的真实世界数据。而真实世界的数据中有重复项即使在应该是唯一的字段中也是如此。
本节描述了重复标签如何改变某些操作的行为以及如何在操作过程中防止重复项的出现或者在出现重复项时如何检测它们。
In [1]: import pandas as pdIn [2]: import numpy as np 重复标签的后果
一些 pandas 方法例如Series.reindex()在存在重复项时根本无法工作。输出无法确定因此 pandas 会引发异常。
In [3]: s1 pd.Series([0, 1, 2], index[a, b, b])In [4]: s1.reindex([a, b, c])
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[4], line 1
---- 1 s1.reindex([a, b, c])File ~/work/pandas/pandas/pandas/core/series.py:5153, in Series.reindex(self, index, axis, method, copy, level, fill_value, limit, tolerance)5136 doc(5137 NDFrame.reindex, # type: ignore[has-type]5138 klass_shared_doc_kwargs[klass],(...)5151 toleranceNone,5152 ) - Series:
- 5153 return super().reindex(5154 indexindex,5155 methodmethod,5156 copycopy,5157 levellevel,5158 fill_valuefill_value,5159 limitlimit,5160 tolerancetolerance,5161 )File ~/work/pandas/pandas/pandas/core/generic.py:5610, in NDFrame.reindex(self, labels, index, columns, axis, method, copy, level, fill_value, limit, tolerance)5607 return self._reindex_multi(axes, copy, fill_value)5609 # perform the reindex on the axes
- 5610 return self._reindex_axes(5611 axes, level, limit, tolerance, method, fill_value, copy5612 ).__finalize__(self, methodreindex)File ~/work/pandas/pandas/pandas/core/generic.py:5633, in NDFrame._reindex_axes(self, axes, level, limit, tolerance, method, fill_value, copy)5630 continue5632 ax self._get_axis(a)
- 5633 new_index, indexer ax.reindex(5634 labels, levellevel, limitlimit, tolerancetolerance, methodmethod5635 )5637 axis self._get_axis_number(a)5638 obj obj._reindex_with_indexers(5639 {axis: [new_index, indexer]},5640 fill_valuefill_value,5641 copycopy,5642 allow_dupsFalse,5643 )File ~/work/pandas/pandas/pandas/core/indexes/base.py:4429, in Index.reindex(self, target, method, level, limit, tolerance)4426 raise ValueError(cannot handle a non-unique multi-index!)4427 elif not self.is_unique:4428 # GH#42568
- 4429 raise ValueError(cannot reindex on an axis with duplicate labels)4430 else:4431 indexer, _ self.get_indexer_non_unique(target)ValueError: cannot reindex on an axis with duplicate labels 其他方法如索引可能会产生非常令人惊讶的结果。通常使用标量进行索引会降低维度。使用标量切片DataFrame将返回一个Series。使用标量切片Series将返回一个标量。但是对于重复项情况并非如此。
In [5]: df1 pd.DataFrame([[0, 1, 2], [3, 4, 5]], columns[A, A, B])In [6]: df1
Out[6]: A A B
0 0 1 2
1 3 4 5 我们的列中有重复项。如果我们切片B我们会得到一个Series
In [7]: df1[B] # a series
Out[7]:
0 2
1 5
Name: B, dtype: int64 但是切片A返回一个DataFrame
In [8]: df1[A] # a DataFrame
Out[8]: A A
0 0 1
1 3 4 这也适用于行标签
In [9]: df2 pd.DataFrame({A: [0, 1, 2]}, index[a, a, b])In [10]: df2
Out[10]: A
a 0
a 1
b 2In [11]: df2.loc[b, A] # a scalar
Out[11]: 2In [12]: df2.loc[a, A] # a Series
Out[12]:
a 0
a 1
Name: A, dtype: int64 重复标签检测
您可以使用Index.is_unique检查Index存储行或列标签是否唯一
In [13]: df2
Out[13]: A
a 0
a 1
b 2In [14]: df2.index.is_unique
Out[14]: FalseIn [15]: df2.columns.is_unique
Out[15]: True 注意
检查索引是否唯一对于大型数据集来说有点昂贵。pandas 会缓存此结果因此在相同的索引上重新检查非常快。
Index.duplicated()将返回一个布尔数组指示标签是否重复。
In [16]: df2.index.duplicated()
Out[16]: array([False, True, False]) 可以用作布尔过滤器来删除重复行。
In [17]: df2.loc[~df2.index.duplicated(), :]
Out[17]: A
a 0
b 2 如果您需要额外的逻辑来处理重复标签而不仅仅是删除重复项则在索引上使用groupby()是一个常见的技巧。例如我们将通过取具有相同标签的所有行的平均值来解决重复项。
In [18]: df2.groupby(level0).mean()
Out[18]: A
a 0.5
b 2.0 禁止重复标签
版本 1.2.0 中的新功能。
如上所述在读取原始数据时处理重复项是一个重要的功能。也就是说您可能希望避免在数据处理管道中引入重复项从方法如pandas.concat()、rename()等。Series和DataFrame通过调用.set_flags(allows_duplicate_labelsFalse)禁止重复标签默认情况下允许。如果存在重复标签将引发异常。
In [19]: pd.Series([0, 1, 2], index[a, b, b]).set_flags(allows_duplicate_labelsFalse)
---------------------------------------------------------------------------
DuplicateLabelError Traceback (most recent call last)
Cell In[19], line 1
---- 1 pd.Series([0, 1, 2], index[a, b, b]).set_flags(allows_duplicate_labelsFalse)File ~/work/pandas/pandas/pandas/core/generic.py:508, in NDFrame.set_flags(self, copy, allows_duplicate_labels)506 df self.copy(deepcopy and not using_copy_on_write())507 if allows_duplicate_labels is not None:
-- 508 df.flags[allows_duplicate_labels] allows_duplicate_labels509 return dfFile ~/work/pandas/pandas/pandas/core/flags.py:109, in Flags.__setitem__(self, key, value)107 if key not in self._keys:108 raise ValueError(fUnknown flag {key}. Must be one of {self._keys})
-- 109 setattr(self, key, value)File ~/work/pandas/pandas/pandas/core/flags.py:96, in Flags.allows_duplicate_labels(self, value)94 if not value:95 for ax in obj.axes:
--- 96 ax._maybe_check_unique()98 self._allows_duplicate_labels valueFile ~/work/pandas/pandas/pandas/core/indexes/base.py:715, in Index._maybe_check_unique(self)712 duplicates self._format_duplicate_message()713 msg f\n{duplicates}
-- 715 raise DuplicateLabelError(msg)DuplicateLabelError: Index has duplicates.positions
label
b [1, 2] 这适用于DataFrame的行和列标签
In [20]: pd.DataFrame([[0, 1, 2], [3, 4, 5]], columns[A, B, C],).set_flags(....: allows_duplicate_labelsFalse....: )....:
Out[20]: A B C
0 0 1 2
1 3 4 5 可以使用allows_duplicate_labels来检查或设置此属性该属性指示该对象是否可以具有重复标签。
In [21]: df pd.DataFrame({A: [0, 1, 2, 3]}, index[x, y, X, Y]).set_flags(....: allows_duplicate_labelsFalse....: )....: In [22]: df
Out[22]: A
x 0
y 1
X 2
Y 3In [23]: df.flags.allows_duplicate_labels
Out[23]: False DataFrame.set_flags()可用于返回一个新的DataFrame其中包含allows_duplicate_labels等属性设置为某个值
In [24]: df2 df.set_flags(allows_duplicate_labelsTrue)In [25]: df2.flags.allows_duplicate_labels
Out[25]: True 返回的新DataFrame是对旧DataFrame上相同数据的视图。或者该属性可以直接设置在同一对象上。
In [26]: df2.flags.allows_duplicate_labels FalseIn [27]: df2.flags.allows_duplicate_labels
Out[27]: False 在处理原始杂乱数据时您可能首先会读取杂乱数据其中可能存在重复标签然后去重并且在之后禁止重复以确保您的数据流水线不会引入重复。 raw pd.read_csv(...)deduplicated raw.groupby(level0).first() # remove duplicatesdeduplicated.flags.allows_duplicate_labels False # disallow going forward 在具有重复标签的Series或DataFrame上设置allows_duplicate_labelsFalse或执行引入重复标签的操作会导致引发errors.DuplicateLabelError。
In [28]: df.rename(str.upper)
---------------------------------------------------------------------------
DuplicateLabelError Traceback (most recent call last)
Cell In[28], line 1
---- 1 df.rename(str.upper)File ~/work/pandas/pandas/pandas/core/frame.py:5767, in DataFrame.rename(self, mapper, index, columns, axis, copy, inplace, level, errors)5636 def rename(5637 self,5638 mapper: Renamer | None None,(...)5646 errors: IgnoreRaise ignore,5647 ) - DataFrame | None:5648 5649 Rename columns or index labels.5650 (...)5765 4 3 65766
- 5767 return super()._rename(5768 mappermapper,5769 indexindex,5770 columnscolumns,5771 axisaxis,5772 copycopy,5773 inplaceinplace,5774 levellevel,5775 errorserrors,5776 )File ~/work/pandas/pandas/pandas/core/generic.py:1140, in NDFrame._rename(self, mapper, index, columns, axis, copy, inplace, level, errors)1138 return None1139 else:
- 1140 return result.__finalize__(self, methodrename)File ~/work/pandas/pandas/pandas/core/generic.py:6262, in NDFrame.__finalize__(self, other, method, **kwargs)6255 if other.attrs:6256 # We want attrs propagation to have minimal performance6257 # impact if attrs are not used; i.e. attrs is an empty dict.6258 # One could make the deepcopy unconditionally, but a deepcopy6259 # of an empty dict is 50x more expensive than the empty check.6260 self.attrs deepcopy(other.attrs)
- 6262 self.flags.allows_duplicate_labels other.flags.allows_duplicate_labels6263 # For subclasses using _metadata.6264 for name in set(self._metadata) set(other._metadata):File ~/work/pandas/pandas/pandas/core/flags.py:96, in Flags.allows_duplicate_labels(self, value)94 if not value:95 for ax in obj.axes:
--- 96 ax._maybe_check_unique()98 self._allows_duplicate_labels valueFile ~/work/pandas/pandas/pandas/core/indexes/base.py:715, in Index._maybe_check_unique(self)712 duplicates self._format_duplicate_message()713 msg f\n{duplicates}
-- 715 raise DuplicateLabelError(msg)DuplicateLabelError: Index has duplicates.positions
label
X [0, 2]
Y [1, 3] 此错误消息包含重复的标签以及Series或DataFrame中所有重复项包括“原始”的数字位置
重复标签传播
一般来说不允许重复是“粘性的”。它会通过操作保留下来。
In [29]: s1 pd.Series(0, index[a, b]).set_flags(allows_duplicate_labelsFalse)In [30]: s1
Out[30]:
a 0
b 0
dtype: int64In [31]: s1.head().rename({a: b})
---------------------------------------------------------------------------
DuplicateLabelError Traceback (most recent call last)
Cell In[31], line 1
---- 1 s1.head().rename({a: b})File ~/work/pandas/pandas/pandas/core/series.py:5090, in Series.rename(self, index, axis, copy, inplace, level, errors)5083 axis self._get_axis_number(axis)5085 if callable(index) or is_dict_like(index):5086 # error: Argument 1 to _rename of NDFrame has incompatible5087 # type Union[Union[Mapping[Any, Hashable], Callable[[Any],5088 # Hashable]], Hashable, None]; expected Union[Mapping[Any,5089 # Hashable], Callable[[Any], Hashable], None]
- 5090 return super()._rename(5091 index, # type: ignore[arg-type]5092 copycopy,5093 inplaceinplace,5094 levellevel,5095 errorserrors,5096 )5097 else:5098 return self._set_name(index, inplaceinplace, deepcopy)File ~/work/pandas/pandas/pandas/core/generic.py:1140, in NDFrame._rename(self, mapper, index, columns, axis, copy, inplace, level, errors)1138 return None1139 else:
- 1140 return result.__finalize__(self, methodrename)File ~/work/pandas/pandas/pandas/core/generic.py:6262, in NDFrame.__finalize__(self, other, method, **kwargs)6255 if other.attrs:6256 # We want attrs propagation to have minimal performance6257 # impact if attrs are not used; i.e. attrs is an empty dict.6258 # One could make the deepcopy unconditionally, but a deepcopy6259 # of an empty dict is 50x more expensive than the empty check.6260 self.attrs deepcopy(other.attrs)
- 6262 self.flags.allows_duplicate_labels other.flags.allows_duplicate_labels6263 # For subclasses using _metadata.6264 for name in set(self._metadata) set(other._metadata):File ~/work/pandas/pandas/pandas/core/flags.py:96, in Flags.allows_duplicate_labels(self, value)94 if not value:95 for ax in obj.axes:
--- 96 ax._maybe_check_unique()98 self._allows_duplicate_labels valueFile ~/work/pandas/pandas/pandas/core/indexes/base.py:715, in Index._maybe_check_unique(self)712 duplicates self._format_duplicate_message()713 msg f\n{duplicates}
-- 715 raise DuplicateLabelError(msg)DuplicateLabelError: Index has duplicates.positions
label
b [0, 1] 警告
这是一个实验性功能。目前许多方法未能传播allows_duplicate_labels的值。未来版本预计每个接受或返回一个或多个 DataFrame 或 Series 对象的方法都将传播allows_duplicate_labels。
重复标签的后果
一些 pandas 方法例如Series.reindex()在存在重复时无法正常工作。输出结果无法确定因此 pandas 会报错。
In [3]: s1 pd.Series([0, 1, 2], index[a, b, b])In [4]: s1.reindex([a, b, c])
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[4], line 1
---- 1 s1.reindex([a, b, c])File ~/work/pandas/pandas/pandas/core/series.py:5153, in Series.reindex(self, index, axis, method, copy, level, fill_value, limit, tolerance)5136 doc(5137 NDFrame.reindex, # type: ignore[has-type]5138 klass_shared_doc_kwargs[klass],(...)5151 toleranceNone,5152 ) - Series:
- 5153 return super().reindex(5154 indexindex,5155 methodmethod,5156 copycopy,5157 levellevel,5158 fill_valuefill_value,5159 limitlimit,5160 tolerancetolerance,5161 )File ~/work/pandas/pandas/pandas/core/generic.py:5610, in NDFrame.reindex(self, labels, index, columns, axis, method, copy, level, fill_value, limit, tolerance)5607 return self._reindex_multi(axes, copy, fill_value)5609 # perform the reindex on the axes
- 5610 return self._reindex_axes(5611 axes, level, limit, tolerance, method, fill_value, copy5612 ).__finalize__(self, methodreindex)File ~/work/pandas/pandas/pandas/core/generic.py:5633, in NDFrame._reindex_axes(self, axes, level, limit, tolerance, method, fill_value, copy)5630 continue5632 ax self._get_axis(a)
- 5633 new_index, indexer ax.reindex(5634 labels, levellevel, limitlimit, tolerancetolerance, methodmethod5635 )5637 axis self._get_axis_number(a)5638 obj obj._reindex_with_indexers(5639 {axis: [new_index, indexer]},5640 fill_valuefill_value,5641 copycopy,5642 allow_dupsFalse,5643 )File ~/work/pandas/pandas/pandas/core/indexes/base.py:4429, in Index.reindex(self, target, method, level, limit, tolerance)4426 raise ValueError(cannot handle a non-unique multi-index!)4427 elif not self.is_unique:4428 # GH#42568
- 4429 raise ValueError(cannot reindex on an axis with duplicate labels)4430 else:4431 indexer, _ self.get_indexer_non_unique(target)ValueError: cannot reindex on an axis with duplicate labels 其他方法如索引可能会产生非常奇怪的结果。通常使用标量进行索引将减少维度。使用标量对DataFrame进行切片将返回一个Series。使用标量对Series进行切片将返回一个标量。但是对于重复项情况并非如此。
In [5]: df1 pd.DataFrame([[0, 1, 2], [3, 4, 5]], columns[A, A, B])In [6]: df1
Out[6]: A A B
0 0 1 2
1 3 4 5 我们在列中有重复。如果我们切片B我们会得到一个Series
In [7]: df1[B] # a series
Out[7]:
0 2
1 5
Name: B, dtype: int64 但是切片A会返回一个DataFrame
In [8]: df1[A] # a DataFrame
Out[8]: A A
0 0 1
1 3 4 这也适用于行标签
In [9]: df2 pd.DataFrame({A: [0, 1, 2]}, index[a, a, b])In [10]: df2
Out[10]: A
a 0
a 1
b 2In [11]: df2.loc[b, A] # a scalar
Out[11]: 2In [12]: df2.loc[a, A] # a Series
Out[12]:
a 0
a 1
Name: A, dtype: int64 重复标签检测
您可以使用Index.is_unique检查Index存储行或列标签是否唯一
In [13]: df2
Out[13]: A
a 0
a 1
b 2In [14]: df2.index.is_unique
Out[14]: FalseIn [15]: df2.columns.is_unique
Out[15]: True 注意
检查索引是否唯一对于大型数据集来说是比较昂贵的。pandas 会缓存此结果因此在相同的索引上重新检查非常快。
Index.duplicated()会返回一个布尔型 ndarray指示标签是否重复。
In [16]: df2.index.duplicated()
Out[16]: array([False, True, False]) 可以将其用作布尔过滤器以删除重复行。
In [17]: df2.loc[~df2.index.duplicated(), :]
Out[17]: A
a 0
b 2 如果您需要额外的逻辑来处理重复标签而不仅仅是删除重复项则在索引上使用groupby()是一种常见的技巧。例如我们将通过取具有相同标签的所有行的平均值来解决重复项。
In [18]: df2.groupby(level0).mean()
Out[18]: A
a 0.5
b 2.0 不允许重复标签
新版本 1.2.0 中新增。
如上所述在读取原始数据时处理重复是一个重要功能。也就是说您可能希望避免在数据处理流水线中引入重复从方法如pandas.concat()rename()等。通过调用.set_flags(allows_duplicate_labelsFalse)Series和DataFrame都不允许重复标签默认允许。如果存在重复标签将引发异常。
In [19]: pd.Series([0, 1, 2], index[a, b, b]).set_flags(allows_duplicate_labelsFalse)
---------------------------------------------------------------------------
DuplicateLabelError Traceback (most recent call last)
Cell In[19], line 1
---- 1 pd.Series([0, 1, 2], index[a, b, b]).set_flags(allows_duplicate_labelsFalse)File ~/work/pandas/pandas/pandas/core/generic.py:508, in NDFrame.set_flags(self, copy, allows_duplicate_labels)506 df self.copy(deepcopy and not using_copy_on_write())507 if allows_duplicate_labels is not None:
-- 508 df.flags[allows_duplicate_labels] allows_duplicate_labels509 return dfFile ~/work/pandas/pandas/pandas/core/flags.py:109, in Flags.__setitem__(self, key, value)107 if key not in self._keys:108 raise ValueError(fUnknown flag {key}. Must be one of {self._keys})
-- 109 setattr(self, key, value)File ~/work/pandas/pandas/pandas/core/flags.py:96, in Flags.allows_duplicate_labels(self, value)94 if not value:95 for ax in obj.axes:
--- 96 ax._maybe_check_unique()98 self._allows_duplicate_labels valueFile ~/work/pandas/pandas/pandas/core/indexes/base.py:715, in Index._maybe_check_unique(self)712 duplicates self._format_duplicate_message()713 msg f\n{duplicates}
-- 715 raise DuplicateLabelError(msg)DuplicateLabelError: Index has duplicates.positions
label
b [1, 2] 这适用于DataFrame的行标签和列标签。
In [20]: pd.DataFrame([[0, 1, 2], [3, 4, 5]], columns[A, B, C],).set_flags(....: allows_duplicate_labelsFalse....: )....:
Out[20]: A B C
0 0 1 2
1 3 4 5 可以使用allows_duplicate_labels来检查或设置此属性该属性指示该对象是否可以具有重复标签。
In [21]: df pd.DataFrame({A: [0, 1, 2, 3]}, index[x, y, X, Y]).set_flags(....: allows_duplicate_labelsFalse....: )....: In [22]: df
Out[22]: A
x 0
y 1
X 2
Y 3In [23]: df.flags.allows_duplicate_labels
Out[23]: False DataFrame.set_flags()可用于返回一个新的DataFrame其中属性如allows_duplicate_labels设置为某个值。
In [24]: df2 df.set_flags(allows_duplicate_labelsTrue)In [25]: df2.flags.allows_duplicate_labels
Out[25]: True 返回的新DataFrame是与旧DataFrame相同数据的视图。或者该属性可以直接设置在同一对象上。
In [26]: df2.flags.allows_duplicate_labels FalseIn [27]: df2.flags.allows_duplicate_labels
Out[27]: False 在处理原始混乱数据时您可能首先读取混乱数据可能具有重复标签去重然后禁止未来出现重复以确保您的数据流水线不会引入重复。 raw pd.read_csv(...)deduplicated raw.groupby(level0).first() # remove duplicatesdeduplicated.flags.allows_duplicate_labels False # disallow going forward 设置allows_duplicate_labelsFalse在具有重复标签的Series或DataFrame上或者在Series或DataFrame上执行引入重复标签的操作而该Series或DataFrame不允许重复标签时将引发errors.DuplicateLabelError。
In [28]: df.rename(str.upper)
---------------------------------------------------------------------------
DuplicateLabelError Traceback (most recent call last)
Cell In[28], line 1
---- 1 df.rename(str.upper)File ~/work/pandas/pandas/pandas/core/frame.py:5767, in DataFrame.rename(self, mapper, index, columns, axis, copy, inplace, level, errors)5636 def rename(5637 self,5638 mapper: Renamer | None None,(...)5646 errors: IgnoreRaise ignore,5647 ) - DataFrame | None:5648 5649 Rename columns or index labels.5650 (...)5765 4 3 65766
- 5767 return super()._rename(5768 mappermapper,5769 indexindex,5770 columnscolumns,5771 axisaxis,5772 copycopy,5773 inplaceinplace,5774 levellevel,5775 errorserrors,5776 )File ~/work/pandas/pandas/pandas/core/generic.py:1140, in NDFrame._rename(self, mapper, index, columns, axis, copy, inplace, level, errors)1138 return None1139 else:
- 1140 return result.__finalize__(self, methodrename)File ~/work/pandas/pandas/pandas/core/generic.py:6262, in NDFrame.__finalize__(self, other, method, **kwargs)6255 if other.attrs:6256 # We want attrs propagation to have minimal performance6257 # impact if attrs are not used; i.e. attrs is an empty dict.6258 # One could make the deepcopy unconditionally, but a deepcopy6259 # of an empty dict is 50x more expensive than the empty check.6260 self.attrs deepcopy(other.attrs)
- 6262 self.flags.allows_duplicate_labels other.flags.allows_duplicate_labels6263 # For subclasses using _metadata.6264 for name in set(self._metadata) set(other._metadata):File ~/work/pandas/pandas/pandas/core/flags.py:96, in Flags.allows_duplicate_labels(self, value)94 if not value:95 for ax in obj.axes:
--- 96 ax._maybe_check_unique()98 self._allows_duplicate_labels valueFile ~/work/pandas/pandas/pandas/core/indexes/base.py:715, in Index._maybe_check_unique(self)712 duplicates self._format_duplicate_message()713 msg f\n{duplicates}
-- 715 raise DuplicateLabelError(msg)DuplicateLabelError: Index has duplicates.positions
label
X [0, 2]
Y [1, 3] 此错误消息包含重复的标签以及所有重复项包括“原始”在Series或DataFrame中的数值位置。
重复标签传播
一般来说禁止重复是“粘性”的。它会通过操作保留下来。
In [29]: s1 pd.Series(0, index[a, b]).set_flags(allows_duplicate_labelsFalse)In [30]: s1
Out[30]:
a 0
b 0
dtype: int64In [31]: s1.head().rename({a: b})
---------------------------------------------------------------------------
DuplicateLabelError Traceback (most recent call last)
Cell In[31], line 1
---- 1 s1.head().rename({a: b})File ~/work/pandas/pandas/pandas/core/series.py:5090, in Series.rename(self, index, axis, copy, inplace, level, errors)5083 axis self._get_axis_number(axis)5085 if callable(index) or is_dict_like(index):5086 # error: Argument 1 to _rename of NDFrame has incompatible5087 # type Union[Union[Mapping[Any, Hashable], Callable[[Any],5088 # Hashable]], Hashable, None]; expected Union[Mapping[Any,5089 # Hashable], Callable[[Any], Hashable], None]
- 5090 return super()._rename(5091 index, # type: ignore[arg-type]5092 copycopy,5093 inplaceinplace,5094 levellevel,5095 errorserrors,5096 )5097 else:5098 return self._set_name(index, inplaceinplace, deepcopy)File ~/work/pandas/pandas/pandas/core/generic.py:1140, in NDFrame._rename(self, mapper, index, columns, axis, copy, inplace, level, errors)1138 return None1139 else:
- 1140 return result.__finalize__(self, methodrename)File ~/work/pandas/pandas/pandas/core/generic.py:6262, in NDFrame.__finalize__(self, other, method, **kwargs)6255 if other.attrs:6256 # We want attrs propagation to have minimal performance6257 # impact if attrs are not used; i.e. attrs is an empty dict.6258 # One could make the deepcopy unconditionally, but a deepcopy6259 # of an empty dict is 50x more expensive than the empty check.6260 self.attrs deepcopy(other.attrs)
- 6262 self.flags.allows_duplicate_labels other.flags.allows_duplicate_labels6263 # For subclasses using _metadata.6264 for name in set(self._metadata) set(other._metadata):File ~/work/pandas/pandas/pandas/core/flags.py:96, in Flags.allows_duplicate_labels(self, value)94 if not value:95 for ax in obj.axes:
--- 96 ax._maybe_check_unique()98 self._allows_duplicate_labels valueFile ~/work/pandas/pandas/pandas/core/indexes/base.py:715, in Index._maybe_check_unique(self)712 duplicates self._format_duplicate_message()713 msg f\n{duplicates}
-- 715 raise DuplicateLabelError(msg)DuplicateLabelError: Index has duplicates.positions
label
b [0, 1] 警告
这是一个实验性功能。目前许多方法未能传播allows_duplicate_labels值。在未来版本中预计每个接受或返回一个或多个 DataFrame 或 Series 对象的方法将传播allows_duplicate_labels。
重复标签传播
一般来说禁止重复是“粘性”的。它会通过操作保留下来。
In [29]: s1 pd.Series(0, index[a, b]).set_flags(allows_duplicate_labelsFalse)In [30]: s1
Out[30]:
a 0
b 0
dtype: int64In [31]: s1.head().rename({a: b})
---------------------------------------------------------------------------
DuplicateLabelError Traceback (most recent call last)
Cell In[31], line 1
---- 1 s1.head().rename({a: b})File ~/work/pandas/pandas/pandas/core/series.py:5090, in Series.rename(self, index, axis, copy, inplace, level, errors)5083 axis self._get_axis_number(axis)5085 if callable(index) or is_dict_like(index):5086 # error: Argument 1 to _rename of NDFrame has incompatible5087 # type Union[Union[Mapping[Any, Hashable], Callable[[Any],5088 # Hashable]], Hashable, None]; expected Union[Mapping[Any,5089 # Hashable], Callable[[Any], Hashable], None]
- 5090 return super()._rename(5091 index, # type: ignore[arg-type]5092 copycopy,5093 inplaceinplace,5094 levellevel,5095 errorserrors,5096 )5097 else:5098 return self._set_name(index, inplaceinplace, deepcopy)File ~/work/pandas/pandas/pandas/core/generic.py:1140, in NDFrame._rename(self, mapper, index, columns, axis, copy, inplace, level, errors)1138 return None1139 else:
- 1140 return result.__finalize__(self, methodrename)File ~/work/pandas/pandas/pandas/core/generic.py:6262, in NDFrame.__finalize__(self, other, method, **kwargs)6255 if other.attrs:6256 # We want attrs propagation to have minimal performance6257 # impact if attrs are not used; i.e. attrs is an empty dict.6258 # One could make the deepcopy unconditionally, but a deepcopy6259 # of an empty dict is 50x more expensive than the empty check.6260 self.attrs deepcopy(other.attrs)
- 6262 self.flags.allows_duplicate_labels other.flags.allows_duplicate_labels6263 # For subclasses using _metadata.6264 for name in set(self._metadata) set(other._metadata):File ~/work/pandas/pandas/pandas/core/flags.py:96, in Flags.allows_duplicate_labels(self, value)94 if not value:95 for ax in obj.axes:
--- 96 ax._maybe_check_unique()98 self._allows_duplicate_labels valueFile ~/work/pandas/pandas/pandas/core/indexes/base.py:715, in Index._maybe_check_unique(self)712 duplicates self._format_duplicate_message()713 msg f\n{duplicates}
-- 715 raise DuplicateLabelError(msg)DuplicateLabelError: Index has duplicates.positions
label
b [0, 1] 警告
这是一个实验性功能。目前许多方法未能传播allows_duplicate_labels值。在未来版本中预计每个接受或返回一个或多个 DataFrame 或 Series 对象的方法将传播allows_duplicate_labels。
分类数据 原文pandas.pydata.org/docs/user_guide/categorical.html 这是关于 pandas 分类数据类型的介绍包括与 R 的factor的简短比较。
Categoricals是一种与统计学中的分类变量对应的 pandas 数据类型。分类变量只能取有限且通常固定的可能值categories在 R 中称为levels。例如性别、社会阶层、血型、国家隶属、观察时间或通过 Likert 量表进行评分等。
与统计学中的分类变量相反分类数据可能具有顺序例如‘强烈同意’与‘同意’或‘第一次观察’与‘第二次观察’但不支持数值运算加法、除法等。
分类数据的所有值都在categories或np.nan中。顺序由categories的顺序而不是值的词法顺序定义。在内部数据结构由一个categories数组和一个指向categories数组中实际值的整数数组codes组成。
分类数据类型在以下情况下很有用 由仅包含几个不同值的字符串变量组成。将这样的字符串变量转换为分类变量将节省一些内存参见这里。 变量的词法顺序与逻辑顺序“one”、“two”、“three”不同。通过转换为分类变量并在类别上指定顺序排序和最小/最大值将使用逻辑顺序而不是词法顺序参见这里。 作为向其他 Python 库发出信号的方式表明该列应被视为分类变量例如使用适当的统计方法或绘图类型。
另请参阅 categoricals 的 API 文档。
对象创建
创建 Series
可以通过几种方式创建Series或DataFrame中的分类变量
在构建Series时指定dtypecategory
In [1]: s pd.Series([a, b, c, a], dtypecategory)In [2]: s
Out[2]:
0 a
1 b
2 c
3 a
dtype: category
Categories (3, object): [a, b, c] 通过将现有的Series或列转换为category数据类型
In [3]: df pd.DataFrame({A: [a, b, c, a]})In [4]: df[B] df[A].astype(category)In [5]: df
Out[5]: A B
0 a a
1 b b
2 c c
3 a a 通过使用特殊函数例如cut()将数据分组为离散的箱。请参阅文档中有关切片的示例。
In [6]: df pd.DataFrame({value: np.random.randint(0, 100, 20)})In [7]: labels [{0} - {1}.format(i, i 9) for i in range(0, 100, 10)]In [8]: df[group] pd.cut(df.value, range(0, 105, 10), rightFalse, labelslabels)In [9]: df.head(10)
Out[9]: value group
0 65 60 - 69
1 49 40 - 49
2 56 50 - 59
3 43 40 - 49
4 43 40 - 49
5 91 90 - 99
6 32 30 - 39
7 87 80 - 89
8 36 30 - 39
9 8 0 - 9 通过将pandas.Categorical对象传递给Series或将其分配给DataFrame。
In [10]: raw_cat pd.Categorical(....: [a, b, c, a], categories[b, c, d], orderedFalse....: )....: In [11]: s pd.Series(raw_cat)In [12]: s
Out[12]:
0 NaN
1 b
2 c
3 NaN
dtype: category
Categories (3, object): [b, c, d]In [13]: df pd.DataFrame({A: [a, b, c, a]})In [14]: df[B] raw_catIn [15]: df
Out[15]: A B
0 a NaN
1 b b
2 c c
3 a NaN 分类数据具有特定的category dtype
In [16]: df.dtypes
Out[16]:
A object
B category
dtype: object DataFrame 创建
类似于前一节中将单个列转换为分类变量的情况DataFrame中的所有列都可以在构建期间或构建后批量转换为分类变量。
可以在构建期间通过在DataFrame构造函数中指定dtypecategory来完成此操作
In [17]: df pd.DataFrame({A: list(abca), B: list(bccd)}, dtypecategory)In [18]: df.dtypes
Out[18]:
A category
B category
dtype: object 请注意每列中存在的类别不同转换是逐列进行的因此只有给定列中存在的标签才是类别
In [19]: df[A]
Out[19]:
0 a
1 b
2 c
3 a
Name: A, dtype: category
Categories (3, object): [a, b, c]In [20]: df[B]
Out[20]:
0 b
1 c
2 c
3 d
Name: B, dtype: category
Categories (3, object): [b, c, d] 类似地可以使用DataFrame.astype()来批量转换现有DataFrame中的所有列
In [21]: df pd.DataFrame({A: list(abca), B: list(bccd)})In [22]: df_cat df.astype(category)In [23]: df_cat.dtypes
Out[23]:
A category
B category
dtype: object 这种转换也是逐列进行的
In [24]: df_cat[A]
Out[24]:
0 a
1 b
2 c
3 a
Name: A, dtype: category
Categories (3, object): [a, b, c]In [25]: df_cat[B]
Out[25]:
0 b
1 c
2 c
3 d
Name: B, dtype: category
Categories (3, object): [b, c, d] 控制行为
在上面的示例中我们传递了dtypecategory我们使用了默认行为 类别是从数据中推断出来的。 类别是无序的。
要控制这些行为而不是传递category请使用CategoricalDtype的实例。
In [26]: from pandas.api.types import CategoricalDtypeIn [27]: s pd.Series([a, b, c, a])In [28]: cat_type CategoricalDtype(categories[b, c, d], orderedTrue)In [29]: s_cat s.astype(cat_type)In [30]: s_cat
Out[30]:
0 NaN
1 b
2 c
3 NaN
dtype: category
Categories (3, object): [b c d] 同样CategoricalDtype可以与DataFrame一起使用以确保所有列中的类别保持一致。
In [31]: from pandas.api.types import CategoricalDtypeIn [32]: df pd.DataFrame({A: list(abca), B: list(bccd)})In [33]: cat_type CategoricalDtype(categorieslist(abcd), orderedTrue)In [34]: df_cat df.astype(cat_type)In [35]: df_cat[A]
Out[35]:
0 a
1 b
2 c
3 a
Name: A, dtype: category
Categories (4, object): [a b c d]In [36]: df_cat[B]
Out[36]:
0 b
1 c
2 c
3 d
Name: B, dtype: category
Categories (4, object): [a b c d] 注意
要执行表格级别的转换其中整个DataFrame中的所有标签都用作每列的类别可以通过categories pd.unique(df.to_numpy().ravel())来以编程方式确定categories参数。
如果你已经有了codes和categories你可以使用from_codes()构造函数在正常构造模式下保存因子化步骤
In [37]: splitter np.random.choice([0, 1], 5, p[0.5, 0.5])In [38]: s pd.Series(pd.Categorical.from_codes(splitter, categories[train, test])) 恢复原始数据
要恢复到原始的Series或 NumPy 数组使用Series.astype(original_dtype)或np.asarray(categorical)
In [39]: s pd.Series([a, b, c, a])In [40]: s
Out[40]:
0 a
1 b
2 c
3 a
dtype: objectIn [41]: s2 s.astype(category)In [42]: s2
Out[42]:
0 a
1 b
2 c
3 a
dtype: category
Categories (3, object): [a, b, c]In [43]: s2.astype(str)
Out[43]:
0 a
1 b
2 c
3 a
dtype: objectIn [44]: np.asarray(s2)
Out[44]: array([a, b, c, a], dtypeobject) 注意
与 R 的factor函数相反分类数据不会将输入值转换为字符串类别将以与原始值相同的数据类型结束。
注意
与 R 的factor函数相反目前没有办法在创建时分配/更改标签。在创建后使用categories来更改类别。## CategoricalDtype
类别的类型完全由以下内容描述 categories: 一个唯一值序列没有缺失值 ordered: 一个布尔值
这些信息可以存储在CategoricalDtype中。categories参数是可选的这意味着在创建pandas.Categorical时实际的类别应该从数据中存在的内容中推断出来。默认情况下假定类别是无序的。
In [45]: from pandas.api.types import CategoricalDtypeIn [46]: CategoricalDtype([a, b, c])
Out[46]: CategoricalDtype(categories[a, b, c], orderedFalse, categories_dtypeobject)In [47]: CategoricalDtype([a, b, c], orderedTrue)
Out[47]: CategoricalDtype(categories[a, b, c], orderedTrue, categories_dtypeobject)In [48]: CategoricalDtype()
Out[48]: CategoricalDtype(categoriesNone, orderedFalse, categories_dtypeNone) CategoricalDtype可以在任何需要dtype的地方使用。例如pandas.read_csv()pandas.DataFrame.astype()或者在Series构造函数中。
注意
作为一种便利当你希望类别的默认行为是无序的并且等于数组中存在的集合值时可以在CategoricalDtype的位置使用字符串category。换句话说dtypecategory等同于dtypeCategoricalDtype()。
相等语义
当两个CategoricalDtype实例具有相同的类别和顺序时它们比较相等。当比较两个无序的分类时不考虑categories的顺序。
In [49]: c1 CategoricalDtype([a, b, c], orderedFalse)# Equal, since order is not considered when orderedFalse
In [50]: c1 CategoricalDtype([b, c, a], orderedFalse)
Out[50]: True# Unequal, since the second CategoricalDtype is ordered
In [51]: c1 CategoricalDtype([a, b, c], orderedTrue)
Out[51]: False 所有的CategoricalDtype实例都与字符串category相等。
In [52]: c1 category
Out[52]: True 描述
在分类数据上使用describe()将产生类似于string类型的Series或DataFrame的输出。
In [53]: cat pd.Categorical([a, c, c, np.nan], categories[b, a, c])In [54]: df pd.DataFrame({cat: cat, s: [a, c, c, np.nan]})In [55]: df.describe()
Out[55]: cat s
count 3 3
unique 2 2
top c c
freq 2 2In [56]: df[cat].describe()
Out[56]:
count 3
unique 2
top c
freq 2
Name: cat, dtype: object 使用类别
分类数据具有categories和ordered属性列出了它们可能的值以及排序是否重要。这些属性被公开为s.cat.categories和s.cat.ordered。如果您不手动指定类别和排序它们将从传递的参数中推断出来。
In [57]: s pd.Series([a, b, c, a], dtypecategory)In [58]: s.cat.categories
Out[58]: Index([a, b, c], dtypeobject)In [59]: s.cat.ordered
Out[59]: False 也可以按特定顺序传递类别
In [60]: s pd.Series(pd.Categorical([a, b, c, a], categories[c, b, a]))In [61]: s.cat.categories
Out[61]: Index([c, b, a], dtypeobject)In [62]: s.cat.ordered
Out[62]: False 注意
新的分类数据不会自动排序。您必须显式传递orderedTrue来指示有序的Categorical。
注意
unique()的结果并不总是与Series.cat.categories相同因为Series.unique()有一些保证即它按照出现的顺序返回类别并且仅包含实际存在的值。
In [63]: s pd.Series(list(babc)).astype(CategoricalDtype(list(abcd)))In [64]: s
Out[64]:
0 b
1 a
2 b
3 c
dtype: category
Categories (4, object): [a, b, c, d]# categories
In [65]: s.cat.categories
Out[65]: Index([a, b, c, d], dtypeobject)# uniques
In [66]: s.unique()
Out[66]:
[b, a, c]
Categories (4, object): [a, b, c, d] 重命名类别
通过使用rename_categories()方法来重命名类别
In [67]: s pd.Series([a, b, c, a], dtypecategory)In [68]: s
Out[68]:
0 a
1 b
2 c
3 a
dtype: category
Categories (3, object): [a, b, c]In [69]: new_categories [Group %s % g for g in s.cat.categories]In [70]: s s.cat.rename_categories(new_categories)In [71]: s
Out[71]:
0 Group a
1 Group b
2 Group c
3 Group a
dtype: category
Categories (3, object): [Group a, Group b, Group c]# You can also pass a dict-like object to map the renaming
In [72]: s s.cat.rename_categories({1: x, 2: y, 3: z})In [73]: s
Out[73]:
0 Group a
1 Group b
2 Group c
3 Group a
dtype: category
Categories (3, object): [Group a, Group b, Group c] 注意
与 R 的factor相反分类数据可以具有除字符串以外的其他类型的类别。
类别必须是唯一的否则会引发ValueError
In [74]: try:....: s s.cat.rename_categories([1, 1, 1])....: except ValueError as e:....: print(ValueError:, str(e))....:
ValueError: Categorical categories must be unique 类别也不能是NaN否则会引ValueError
In [75]: try:....: s s.cat.rename_categories([1, 2, np.nan])....: except ValueError as e:....: print(ValueError:, str(e))....:
ValueError: Categorical categories cannot be null 追加新类别
可以通过使用add_categories()方法来追加类别
In [76]: s s.cat.add_categories([4])In [77]: s.cat.categories
Out[77]: Index([Group a, Group b, Group c, 4], dtypeobject)In [78]: s
Out[78]:
0 Group a
1 Group b
2 Group c
3 Group a
dtype: category
Categories (4, object): [Group a, Group b, Group c, 4] 删除类别
通过使用remove_categories()方法可以删除类别。被删除的值将被np.nan替换。
In [79]: s s.cat.remove_categories([4])In [80]: s
Out[80]:
0 Group a
1 Group b
2 Group c
3 Group a
dtype: category
Categories (3, object): [Group a, Group b, Group c] 删除未使用的类别
也可以删除未使用的类别
In [81]: s pd.Series(pd.Categorical([a, b, a], categories[a, b, c, d]))In [82]: s
Out[82]:
0 a
1 b
2 a
dtype: category
Categories (4, object): [a, b, c, d]In [83]: s.cat.remove_unused_categories()
Out[83]:
0 a
1 b
2 a
dtype: category
Categories (2, object): [a, b] 设置类别
如果您想要一次性执行删除和添加新类别的操作这样做有一定的速度优势或者简单地将类别设置为预定义的规模请使用set_categories()。
In [84]: s pd.Series([one, two, four, -], dtypecategory)In [85]: s
Out[85]:
0 one
1 two
2 four
3 -
dtype: category
Categories (4, object): [-, four, one, two]In [86]: s s.cat.set_categories([one, two, three, four])In [87]: s
Out[87]:
0 one
1 two
2 four
3 NaN
dtype: category
Categories (4, object): [one, two, three, four] 注意
请注意Categorical.set_categories()无法知道某个类别是有意省略的还是因为拼写错误或在 Python3 下由于类型差异例如NumPy S1 dtype 和 Python 字符串。这可能导致意外的行为
排序和顺序
如果分类数据是有序的s.cat.ordered True那么类别的顺序具有意义并且可以执行某些操作。如果分类是无序的.min()/.max()将引发TypeError。
In [88]: s pd.Series(pd.Categorical([a, b, c, a], orderedFalse))In [89]: s s.sort_values()In [90]: s pd.Series([a, b, c, a]).astype(CategoricalDtype(orderedTrue))In [91]: s s.sort_values()In [92]: s
Out[92]:
0 a
3 a
1 b
2 c
dtype: category
Categories (3, object): [a b c]In [93]: s.min(), s.max()
Out[93]: (a, c) 您可以使用as_ordered()将分类数据设置为有序或者使用as_unordered()将其设置为无序。这些方法默认会返回一个新对象。
In [94]: s.cat.as_ordered()
Out[94]:
0 a
3 a
1 b
2 c
dtype: category
Categories (3, object): [a b c]In [95]: s.cat.as_unordered()
Out[95]:
0 a
3 a
1 b
2 c
dtype: category
Categories (3, object): [a, b, c] 排序将使用类别定义的顺序而不是数据类型上存在的任何词法顺序。即使对于字符串和数字数据也是如此
In [96]: s pd.Series([1, 2, 3, 1], dtypecategory)In [97]: s s.cat.set_categories([2, 3, 1], orderedTrue)In [98]: s
Out[98]:
0 1
1 2
2 3
3 1
dtype: category
Categories (3, int64): [2 3 1]In [99]: s s.sort_values()In [100]: s
Out[100]:
1 2
2 3
0 1
3 1
dtype: category
Categories (3, int64): [2 3 1]In [101]: s.min(), s.max()
Out[101]: (2, 1) 重新排序
通过Categorical.reorder_categories()和Categorical.set_categories()方法可以重新排序类别。对于Categorical.reorder_categories()所有旧类别必须包含在新类别中不允许有新类别。这将必然使排序顺序与类别顺序相同。
In [102]: s pd.Series([1, 2, 3, 1], dtypecategory)In [103]: s s.cat.reorder_categories([2, 3, 1], orderedTrue)In [104]: s
Out[104]:
0 1
1 2
2 3
3 1
dtype: category
Categories (3, int64): [2 3 1]In [105]: s s.sort_values()In [106]: s
Out[106]:
1 2
2 3
0 1
3 1
dtype: category
Categories (3, int64): [2 3 1]In [107]: s.min(), s.max()
Out[107]: (2, 1) 注意
注意分配新类别和重新排序类别之间的区别第一个重新命名类别因此Series中的个别值也会更名但是如果第一个位置最后被排序重新命名的值仍将最后被排序。重新排序意味着排序值的方式在之后会有所不同但不意味着Series中的个别值已更改。
注意
如果Categorical未排序Series.min()和Series.max()会引发TypeError。数值运算如、-、*、/及基于它们的操作例如Series.median()如果数组的长度为偶数需要计算两个值之间的平均值不起作用并引发TypeError。
多列排序
分类dtyped列将以与其他列类似的方式参与多列排序。分类的排序由该列的categories确定。
In [108]: dfs pd.DataFrame(.....: {.....: A: pd.Categorical(.....: list(bbeebbaa),.....: categories[e, a, b],.....: orderedTrue,.....: ),.....: B: [1, 2, 1, 2, 2, 1, 2, 1],.....: }.....: ).....: In [109]: dfs.sort_values(by[A, B])
Out[109]: A B
2 e 1
3 e 2
7 a 1
6 a 2
0 b 1
5 b 1
1 b 2
4 b 2 重新排序categories会改变未来的排序。
In [110]: dfs[A] dfs[A].cat.reorder_categories([a, b, e])In [111]: dfs.sort_values(by[A, B])
Out[111]: A B
7 a 1
6 a 2
0 b 1
5 b 1
1 b 2
4 b 2
2 e 1
3 e 2 比较
比较分类数据与其他对象可能有三种情况 与类列表对象列表、Series、数组等进行相等比较和!长度与分类数据相同。 所有与另一个分类系列的比较、!、、、和当orderedTrue且categories相同时。 所有分类数据与标量的比较。
所有其他比较特别是两个具有不同类别或一个具有任何类列表对象的分类的“非相等”比较都会引发TypeError。
注意
对分类数据与Series、np.array、list或具有不同类别或排序的分类数据的任何“非相等”比较都会引发TypeError因为自定义类别排序可能会被解释为两种方式一种考虑排序一种不考虑排序。
In [112]: cat pd.Series([1, 2, 3]).astype(CategoricalDtype([3, 2, 1], orderedTrue))In [113]: cat_base pd.Series([2, 2, 2]).astype(CategoricalDtype([3, 2, 1], orderedTrue))In [114]: cat_base2 pd.Series([2, 2, 2]).astype(CategoricalDtype(orderedTrue))In [115]: cat
Out[115]:
0 1
1 2
2 3
dtype: category
Categories (3, int64): [3 2 1]In [116]: cat_base
Out[116]:
0 2
1 2
2 2
dtype: category
Categories (3, int64): [3 2 1]In [117]: cat_base2
Out[117]:
0 2
1 2
2 2
dtype: category
Categories (1, int64): [2] 与具有相同类别和顺序的分类或标量进行比较有效
In [118]: cat cat_base
Out[118]:
0 True
1 False
2 False
dtype: boolIn [119]: cat 2
Out[119]:
0 True
1 False
2 False
dtype: bool 相等比较适用于任何长度相同的类列表对象和标量
In [120]: cat cat_base
Out[120]:
0 False
1 True
2 False
dtype: boolIn [121]: cat np.array([1, 2, 3])
Out[121]:
0 True
1 True
2 True
dtype: boolIn [122]: cat 2
Out[122]:
0 False
1 True
2 False
dtype: bool 这不起作用因为类别不相同
In [123]: try:.....: cat cat_base2.....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Categoricals can only be compared if categories are the same. 如果要对非分类数据进行“非相等”比较需要明确地将分类数据转换回原始值
In [124]: base np.array([1, 2, 3])In [125]: try:.....: cat base.....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Cannot compare a Categorical for op __gt__ with type class numpy.ndarray.
If you want to compare values, use np.asarray(cat) op other.In [126]: np.asarray(cat) base
Out[126]: array([False, False, False]) 当您比较具有相同类别的两个无序分类时不考虑顺序
In [127]: c1 pd.Categorical([a, b], categories[a, b], orderedFalse)In [128]: c2 pd.Categorical([a, b], categories[b, a], orderedFalse)In [129]: c1 c2
Out[129]: array([ True, True]) 操作
除了Series.min(), Series.max() 和 Series.mode()分类数据还可以进行以下操作
Series 方法如Series.value_counts() 会使用所有类别即使数据中有些类别不存在
In [130]: s pd.Series(pd.Categorical([a, b, c, c], categories[c, a, b, d]))In [131]: s.value_counts()
Out[131]:
c 2
a 1
b 1
d 0
Name: count, dtype: int64 DataFrame 方法如DataFrame.sum() 在 observedFalse 时也会显示“未使用”的类别。
In [132]: columns pd.Categorical(.....: [One, One, Two], categories[One, Two, Three], orderedTrue.....: ).....: In [133]: df pd.DataFrame(.....: data[[1, 2, 3], [4, 5, 6]],.....: columnspd.MultiIndex.from_arrays([[A, B, B], columns]),.....: ).T.....: In [134]: df.groupby(level1, observedFalse).sum()
Out[134]: 0 1
One 3 9
Two 3 6
Three 0 0 Groupby 在 observedFalse 时也会显示“未使用”的类别
In [135]: cats pd.Categorical(.....: [a, b, b, b, c, c, c], categories[a, b, c, d].....: ).....: In [136]: df pd.DataFrame({cats: cats, values: [1, 2, 2, 2, 3, 4, 5]})In [137]: df.groupby(cats, observedFalse).mean()
Out[137]: values
cats
a 1.0
b 2.0
c 4.0
d NaNIn [138]: cats2 pd.Categorical([a, a, b, b], categories[a, b, c])In [139]: df2 pd.DataFrame(.....: {.....: cats: cats2,.....: B: [c, d, c, d],.....: values: [1, 2, 3, 4],.....: }.....: ).....: In [140]: df2.groupby([cats, B], observedFalse).mean()
Out[140]: values
cats B
a c 1.0d 2.0
b c 3.0d 4.0
c c NaNd NaN 透视表
In [141]: raw_cat pd.Categorical([a, a, b, b], categories[a, b, c])In [142]: df pd.DataFrame({A: raw_cat, B: [c, d, c, d], values: [1, 2, 3, 4]})In [143]: pd.pivot_table(df, valuesvalues, index[A, B], observedFalse)
Out[143]: values
A B
a c 1.0d 2.0
b c 3.0d 4.0 数据整理
优化的 pandas 数据访问方法 .loc, .iloc, .at, 和 .iat 的工作方式与正常情况下相同。唯一的区别在于返回类型用于获取以及只有已在 categories 中的值才能被赋值。
获取
如果切片操作返回 DataFrame 或 Series 类型的列则 category dtype 会被保留。
In [144]: idx pd.Index([h, i, j, k, l, m, n])In [145]: cats pd.Series([a, b, b, b, c, c, c], dtypecategory, indexidx)In [146]: values [1, 2, 2, 2, 3, 4, 5]In [147]: df pd.DataFrame({cats: cats, values: values}, indexidx)In [148]: df.iloc[2:4, :]
Out[148]: cats values
j b 2
k b 2In [149]: df.iloc[2:4, :].dtypes
Out[149]:
cats category
values int64
dtype: objectIn [150]: df.loc[h:j, cats]
Out[150]:
h a
i b
j b
Name: cats, dtype: category
Categories (3, object): [a, b, c]In [151]: df[df[cats] b]
Out[151]: cats values
i b 2
j b 2
k b 2 类别类型未保留的一个例子是如果您取一行结果的 Series 的 dtype 是 object
# get the complete h row as a Series
In [152]: df.loc[h, :]
Out[152]:
cats a
values 1
Name: h, dtype: object 从分类数据中返回单个项目也会返回该值而不是长度为“1”的分类。
In [153]: df.iat[0, 0]
Out[153]: aIn [154]: df[cats] df[cats].cat.rename_categories([x, y, z])In [155]: df.at[h, cats] # returns a string
Out[155]: x 注意
这与 R 的 factor 函数相反其中 factor(c(1,2,3))[1] 返回一个单个值 factor。
要获得类型为 category 的单个值 Series您需要传入一个包含单个值的列表
In [156]: df.loc[[h], cats]
Out[156]:
h x
Name: cats, dtype: category
Categories (3, object): [x, y, z] 字符串和日期时间访问器
如果 s.cat.categories 是适当类型则访问器 .dt 和 .str 将起作用
In [157]: str_s pd.Series(list(aabb))In [158]: str_cat str_s.astype(category)In [159]: str_cat
Out[159]:
0 a
1 a
2 b
3 b
dtype: category
Categories (2, object): [a, b]In [160]: str_cat.str.contains(a)
Out[160]:
0 True
1 True
2 False
3 False
dtype: boolIn [161]: date_s pd.Series(pd.date_range(1/1/2015, periods5))In [162]: date_cat date_s.astype(category)In [163]: date_cat
Out[163]:
0 2015-01-01
1 2015-01-02
2 2015-01-03
3 2015-01-04
4 2015-01-05
dtype: category
Categories (5, datetime64[ns]): [2015-01-01, 2015-01-02, 2015-01-03, 2015-01-04, 2015-01-05]In [164]: date_cat.dt.day
Out[164]:
0 1
1 2
2 3
3 4
4 5
dtype: int32 注意
返回的 Series或 DataFrame与在该类型的 Series 上使用 .str.method / .dt.method 时的类型相同而不是 category 类型。
这意味着从 Series 的访问器的方法和属性返回的值与将该 Series 转换为 category 类型后的访问器的方法和属性返回的值将相等
In [165]: ret_s str_s.str.contains(a)In [166]: ret_cat str_cat.str.contains(a)In [167]: ret_s.dtype ret_cat.dtype
Out[167]: TrueIn [168]: ret_s ret_cat
Out[168]:
0 True
1 True
2 True
3 True
dtype: bool 注意
工作是在 categories 上进行的然后构建一个新的 Series。如果您有一个字符串类型的 Series其中有很多重复的元素即 Series 中的唯一元素数量远小于 Series 的长度这可能会对性能产生影响。在这种情况下将原始 Series 转换为 category 类型并在其上使用 .str.method 或 .dt.property 可能更快。
设置
设置分类列或 Series中的值只要该值包含在 categories 中即可
In [169]: idx pd.Index([h, i, j, k, l, m, n])In [170]: cats pd.Categorical([a, a, a, a, a, a, a], categories[a, b])In [171]: values [1, 1, 1, 1, 1, 1, 1]In [172]: df pd.DataFrame({cats: cats, values: values}, indexidx)In [173]: df.iloc[2:4, :] [[b, 2], [b, 2]]In [174]: df
Out[174]: cats values
h a 1
i a 1
j b 2
k b 2
l a 1
m a 1
n a 1In [175]: try:.....: df.iloc[2:4, :] [[c, 3], [c, 3]].....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Cannot setitem on a Categorical with a new category, set the categories first 通过分配分类数据来设置值也会检查 categories 是否匹配
In [176]: df.loc[j:k, cats] pd.Categorical([a, a], categories[a, b])In [177]: df
Out[177]: cats values
h a 1
i a 1
j a 2
k a 2
l a 1
m a 1
n a 1In [178]: try:.....: df.loc[j:k, cats] pd.Categorical([b, b], categories[a, b, c]).....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Cannot set a Categorical with another, without identical categories 将Categorical分配给其他类型列的部分将使用这些值
In [179]: df pd.DataFrame({a: [1, 1, 1, 1, 1], b: [a, a, a, a, a]})In [180]: df.loc[1:2, a] pd.Categorical([b, b], categories[a, b])In [181]: df.loc[2:3, b] pd.Categorical([b, b], categories[a, b])In [182]: df
Out[182]: a b
0 1 a
1 b a
2 b b
3 1 b
4 1 aIn [183]: df.dtypes
Out[183]:
a object
b object
dtype: object 合并/连接
默认情况下合并包含相同类别的Series或DataFrames将导致category dtype否则结果将取决于底层类别的 dtype。导致非分类 dtype 的合并可能会导致更高的内存使用量。使用.astype或union_categoricals来确保category结果。
In [184]: from pandas.api.types import union_categoricals# same categories
In [185]: s1 pd.Series([a, b], dtypecategory)In [186]: s2 pd.Series([a, b, a], dtypecategory)In [187]: pd.concat([s1, s2])
Out[187]:
0 a
1 b
0 a
1 b
2 a
dtype: category
Categories (2, object): [a, b]# different categories
In [188]: s3 pd.Series([b, c], dtypecategory)In [189]: pd.concat([s1, s3])
Out[189]:
0 a
1 b
0 b
1 c
dtype: object# Output dtype is inferred based on categories values
In [190]: int_cats pd.Series([1, 2], dtypecategory)In [191]: float_cats pd.Series([3.0, 4.0], dtypecategory)In [192]: pd.concat([int_cats, float_cats])
Out[192]:
0 1.0
1 2.0
0 3.0
1 4.0
dtype: float64In [193]: pd.concat([s1, s3]).astype(category)
Out[193]:
0 a
1 b
0 b
1 c
dtype: category
Categories (3, object): [a, b, c]In [194]: union_categoricals([s1.array, s3.array])
Out[194]:
[a, b, b, c]
Categories (3, object): [a, b, c] 以下表格总结了合并Categoricals的结果
arg1arg2相同结果类别类别True类别类别object类别objectFalseobject推断出的 dtype
| 类别int | 类别float | False | float推断出的 dtype | ### 合并
如果要合并不一定具有相同类别的分类union_categoricals()函数将合并类别列表。新类别将是被合并类别的并集。
In [195]: from pandas.api.types import union_categoricalsIn [196]: a pd.Categorical([b, c])In [197]: b pd.Categorical([a, b])In [198]: union_categoricals([a, b])
Out[198]:
[b, c, a, b]
Categories (3, object): [b, c, a] 默认情况下生成的类别将按照它们在数据中出现的顺序排序。如果要使类别按字典顺序排序请使用sort_categoriesTrue参数。
In [199]: union_categoricals([a, b], sort_categoriesTrue)
Out[199]:
[b, c, a, b]
Categories (3, object): [a, b, c] union_categoricals还适用于将具有相同类别和顺序信息的两个分类合并的“简单”情况例如您也可以使用append。
In [200]: a pd.Categorical([a, b], orderedTrue)In [201]: b pd.Categorical([a, b, a], orderedTrue)In [202]: union_categoricals([a, b])
Out[202]:
[a, b, a, b, a]
Categories (2, object): [a b] 以下引发TypeError因为类别是有序的而且不相同。
In [203]: a pd.Categorical([a, b], orderedTrue)In [204]: b pd.Categorical([a, b, c], orderedTrue)In [205]: union_categoricals([a, b])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[205], line 1
---- 1 union_categoricals([a, b])File ~/work/pandas/pandas/pandas/core/dtypes/concat.py:341, in union_categoricals(to_union, sort_categories, ignore_order)339 if all(c.ordered for c in to_union):340 msg to union ordered Categoricals, all categories must be the same
-- 341 raise TypeError(msg)342 raise TypeError(Categorical.ordered must be the same)344 if ignore_order:TypeError: to union ordered Categoricals, all categories must be the same 可以通过使用ignore_orderedTrue参数来合并具有不同类别或排序的有序分类。
In [206]: a pd.Categorical([a, b, c], orderedTrue)In [207]: b pd.Categorical([c, b, a], orderedTrue)In [208]: union_categoricals([a, b], ignore_orderTrue)
Out[208]:
[a, b, c, c, b, a]
Categories (3, object): [a, b, c] union_categoricals()还适用于CategoricalIndex或包含分类数据的Series但请注意结果数组将始终是普通的Categorical
In [209]: a pd.Series([b, c], dtypecategory)In [210]: b pd.Series([a, b], dtypecategory)In [211]: union_categoricals([a, b])
Out[211]:
[b, c, a, b]
Categories (3, object): [b, c, a] 注意
当合并分类时union_categoricals可能会重新编码类别的整数代码。这可能是您想要的但如果您依赖于类别的确切编号请注意。
In [212]: c1 pd.Categorical([b, c])In [213]: c2 pd.Categorical([a, b])In [214]: c1
Out[214]:
[b, c]
Categories (2, object): [b, c]# b is coded to 0
In [215]: c1.codes
Out[215]: array([0, 1], dtypeint8)In [216]: c2
Out[216]:
[a, b]
Categories (2, object): [a, b]# b is coded to 1
In [217]: c2.codes
Out[217]: array([0, 1], dtypeint8)In [218]: c union_categoricals([c1, c2])In [219]: c
Out[219]:
[b, c, a, b]
Categories (3, object): [b, c, a]# b is coded to 0 throughout, same as c1, different from c2
In [220]: c.codes
Out[220]: array([0, 1, 2, 0], dtypeint8) 获取数据的输入/输出
您可以将包含category dtypes 的数据写入HDFStore。请参见这里以获取示例和注意事项。
也可以将数据写入Stata格式文件并从中读取数据。请参见这里以获取示例和注意事项。
写入 CSV 文件将转换数据有效地删除有关分类类别和排序的任何信息。因此如果您读取回 CSV 文件必须将相关列转换回category并分配正确的类别和类别排序。
In [221]: import ioIn [222]: s pd.Series(pd.Categorical([a, b, b, a, a, d]))# rename the categories
In [223]: s s.cat.rename_categories([very good, good, bad])# reorder the categories and add missing categories
In [224]: s s.cat.set_categories([very bad, bad, medium, good, very good])In [225]: df pd.DataFrame({cats: s, vals: [1, 2, 3, 4, 5, 6]})In [226]: csv io.StringIO()In [227]: df.to_csv(csv)In [228]: df2 pd.read_csv(io.StringIO(csv.getvalue()))In [229]: df2.dtypes
Out[229]:
Unnamed: 0 int64
cats object
vals int64
dtype: objectIn [230]: df2[cats]
Out[230]:
0 very good
1 good
2 good
3 very good
4 very good
5 bad
Name: cats, dtype: object# Redo the category
In [231]: df2[cats] df2[cats].astype(category)In [232]: df2[cats] df2[cats].cat.set_categories(.....: [very bad, bad, medium, good, very good].....: ).....: In [233]: df2.dtypes
Out[233]:
Unnamed: 0 int64
cats category
vals int64
dtype: objectIn [234]: df2[cats]
Out[234]:
0 very good
1 good
2 good
3 very good
4 very good
5 bad
Name: cats, dtype: category
Categories (5, object): [very bad, bad, medium, good, very good] 写入 SQL 数据库时也适用于相同规则。
缺失数据
pandas 主要使用值 np.nan 表示缺失数据。默认情况下不包括在计算中。请参阅缺失数据部分。
缺失值 不应 包含在分类的 categories 中只应包含在 values 中。相反应理解 NaN 是不同的并且始终可能存在。在处理分类的 codes 时缺失值的代码始终为 -1。
In [235]: s pd.Series([a, b, np.nan, a], dtypecategory)# only two categories
In [236]: s
Out[236]:
0 a
1 b
2 NaN
3 a
dtype: category
Categories (2, object): [a, b]In [237]: s.cat.codes
Out[237]:
0 0
1 1
2 -1
3 0
dtype: int8 用于处理缺失数据的方法例如 isna()、fillna()、dropna()都可以正常工作
In [238]: s pd.Series([a, b, np.nan], dtypecategory)In [239]: s
Out[239]:
0 a
1 b
2 NaN
dtype: category
Categories (2, object): [a, b]In [240]: pd.isna(s)
Out[240]:
0 False
1 False
2 True
dtype: boolIn [241]: s.fillna(a)
Out[241]:
0 a
1 b
2 a
dtype: category
Categories (2, object): [a, b] 与 R 的 factor 的差异
以下与 R 的 factor 函数的差异可以观察到 R 的 levels 被命名为 categories。 R 的 levels 始终是字符串类型而 pandas 的 categories 可以是任何 dtype。 不可能在创建时指定标签。之后使用 s.cat.rename_categories(new_labels)。在创建时指定标签。之后使用 s.cat.rename_categories(new_labels)。 与 R 的 factor 函数相反将分类数据作为唯一输入来创建新的分类系列 不会 删除未使用的类别而是创建一个与传入的相等的新分类系列 R 允许在其 levelspandas 的 categories中包含缺失值。pandas 不允许 NaN 类别但缺失值仍然可以在 values 中。
注意事项
内存使用
Categorical 的内存使用量与类别数量加上数据长度成正比。相比之下object dtype 是数据长度的常数倍。
In [242]: s pd.Series([foo, bar] * 1000)# object dtype
In [243]: s.nbytes
Out[243]: 16000# category dtype
In [244]: s.astype(category).nbytes
Out[244]: 2016 注意
如果类别数量接近数据长度Categorical 将使用几乎相同或更多的内存比等效的 object dtype 表示法更多。
In [245]: s pd.Series([foo%04d % i for i in range(2000)])# object dtype
In [246]: s.nbytes
Out[246]: 16000# category dtype
In [247]: s.astype(category).nbytes
Out[247]: 20000 Categorical 不是一个 numpy 数组
目前分类数据和底层的 Categorical 实现为 Python 对象而不是低级别的 NumPy 数组 dtype。这会导致一些问题。
NumPy 本身不知道新的 dtype
In [248]: try:.....: np.dtype(category).....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: data type category not understoodIn [249]: dtype pd.Categorical([a]).dtypeIn [250]: try:.....: np.dtype(dtype).....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Cannot interpret CategoricalDtype(categories[a], orderedFalse, categories_dtypeobject) as a data type Dtype 比较有效
In [251]: dtype np.str_
Out[251]: FalseIn [252]: np.str_ dtype
Out[252]: False 要检查 Series 是否包含分类数据请使用 hasattr(s, cat)
In [253]: hasattr(pd.Series([a], dtypecategory), cat)
Out[253]: TrueIn [254]: hasattr(pd.Series([a]), cat)
Out[254]: False 在类型为 category 的 Series 上使 NumPy 函数应该不起作用因为 Categoricals 不是数值数据即使 .categories 是数值的情况下也是如此。
In [255]: s pd.Series(pd.Categorical([1, 2, 3, 4]))In [256]: try:.....: np.sum(s).....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Categorical with dtype category does not support reduction sum 注意
如果这样的函数有效请在 pandas-dev/pandas 提交 bug
在 apply 中的 dtype
pandas 目前在 apply 函数中不会保留 dtype如果你沿着行应用你会得到一个 Series其 dtype 为 object与获取一行相同 - 获取一个元素将返回基本类型并且沿着列应用也会转换为 object。NaN 值不受影响。你可以在应用函数之前使用 fillna 处理缺失值。
In [257]: df pd.DataFrame(.....: {.....: a: [1, 2, 3, 4],.....: b: [a, b, c, d],.....: cats: pd.Categorical([1, 2, 3, 2]),.....: }.....: ).....: In [258]: df.apply(lambda row: type(row[cats]), axis1)
Out[258]:
0 class int
1 class int
2 class int
3 class int
dtype: objectIn [259]: df.apply(lambda col: col.dtype, axis0)
Out[259]:
a int64
b object
cats category
dtype: object 分类索引
CategoricalIndex是一种支持具有重复索引的索引的类型。这是围绕Categorical的容器允许有效地索引和存储具有大量重复元素的索引。有关更详细的解释请参阅高级索引文档。
设置索引将创建CategoricalIndex
In [260]: cats pd.Categorical([1, 2, 3, 4], categories[4, 2, 3, 1])In [261]: strings [a, b, c, d]In [262]: values [4, 2, 3, 1]In [263]: df pd.DataFrame({strings: strings, values: values}, indexcats)In [264]: df.index
Out[264]: CategoricalIndex([1, 2, 3, 4], categories[4, 2, 3, 1], orderedFalse, dtypecategory)# This now sorts by the categories order
In [265]: df.sort_index()
Out[265]: strings values
4 d 1
2 b 2
3 c 3
1 a 4 副作用
从Categorical构造Series不会复制输入的Categorical。这意味着对Series的更改在大多数情况下会更改原始的Categorical
In [266]: cat pd.Categorical([1, 2, 3, 10], categories[1, 2, 3, 4, 10])In [267]: s pd.Series(cat, namecat)In [268]: cat
Out[268]:
[1, 2, 3, 10]
Categories (5, int64): [1, 2, 3, 4, 10]In [269]: s.iloc[0:2] 10In [270]: cat
Out[270]:
[10, 10, 3, 10]
Categories (5, int64): [1, 2, 3, 4, 10] 使用copyTrue来防止这种行为或者简单地不要重复使用Categoricals
In [271]: cat pd.Categorical([1, 2, 3, 10], categories[1, 2, 3, 4, 10])In [272]: s pd.Series(cat, namecat, copyTrue)In [273]: cat
Out[273]:
[1, 2, 3, 10]
Categories (5, int64): [1, 2, 3, 4, 10]In [274]: s.iloc[0:2] 10In [275]: cat
Out[275]:
[1, 2, 3, 10]
Categories (5, int64): [1, 2, 3, 4, 10] 注意
在某些情况下当您提供 NumPy 数组而不是Categorical时也会发生这种情况使用整数数组例如np.array([1,2,3,4])将表现出相同的行为而使用字符串数组例如np.array([a,b,c,a])则不会。
对象创建
Series 创建
可以通过多种方式创建分类Series或DataFrame中的列
在构造Series时指定dtypecategory
In [1]: s pd.Series([a, b, c, a], dtypecategory)In [2]: s
Out[2]:
0 a
1 b
2 c
3 a
dtype: category
Categories (3, object): [a, b, c] 通过将现有的Series或列转换为category dtype
In [3]: df pd.DataFrame({A: [a, b, c, a]})In [4]: df[B] df[A].astype(category)In [5]: df
Out[5]: A B
0 a a
1 b b
2 c c
3 a a 通过使用特殊函数例如cut()将数据分组为离散的箱。请参阅文档中有关切片的示例。
In [6]: df pd.DataFrame({value: np.random.randint(0, 100, 20)})In [7]: labels [{0} - {1}.format(i, i 9) for i in range(0, 100, 10)]In [8]: df[group] pd.cut(df.value, range(0, 105, 10), rightFalse, labelslabels)In [9]: df.head(10)
Out[9]: value group
0 65 60 - 69
1 49 40 - 49
2 56 50 - 59
3 43 40 - 49
4 43 40 - 49
5 91 90 - 99
6 32 30 - 39
7 87 80 - 89
8 36 30 - 39
9 8 0 - 9 通过将pandas.Categorical对象传递给Series或将其分配给DataFrame。
In [10]: raw_cat pd.Categorical(....: [a, b, c, a], categories[b, c, d], orderedFalse....: )....: In [11]: s pd.Series(raw_cat)In [12]: s
Out[12]:
0 NaN
1 b
2 c
3 NaN
dtype: category
Categories (3, object): [b, c, d]In [13]: df pd.DataFrame({A: [a, b, c, a]})In [14]: df[B] raw_catIn [15]: df
Out[15]: A B
0 a NaN
1 b b
2 c c
3 a NaN 分类数据具有特定的category dtype
In [16]: df.dtypes
Out[16]:
A object
B category
dtype: object DataFrame 创建
类似于前一节中将单个列转换为分类的情况可以在构建过程中或之后将DataFrame中的所有列批量转换为分类。
这可以在构造过程中通过在DataFrame构造函数中指定dtypecategory来完成
In [17]: df pd.DataFrame({A: list(abca), B: list(bccd)}, dtypecategory)In [18]: df.dtypes
Out[18]:
A category
B category
dtype: object 请注意每列中存在的类别不同转换是逐列进行的因此只有给定列中存在的标签才是类别
In [19]: df[A]
Out[19]:
0 a
1 b
2 c
3 a
Name: A, dtype: category
Categories (3, object): [a, b, c]In [20]: df[B]
Out[20]:
0 b
1 c
2 c
3 d
Name: B, dtype: category
Categories (3, object): [b, c, d] 类似地可以使用DataFrame.astype()来批量转换现有DataFrame中的所有列
In [21]: df pd.DataFrame({A: list(abca), B: list(bccd)})In [22]: df_cat df.astype(category)In [23]: df_cat.dtypes
Out[23]:
A category
B category
dtype: object 这种转换也是逐列进行的
In [24]: df_cat[A]
Out[24]:
0 a
1 b
2 c
3 a
Name: A, dtype: category
Categories (3, object): [a, b, c]In [25]: df_cat[B]
Out[25]:
0 b
1 c
2 c
3 d
Name: B, dtype: category
Categories (3, object): [b, c, d] 控制行为
在上面的示中我们传递dtypecategory时使用了默认行为 类别是从数据中推断出来的。 类别是无序的。
要控制这些行为而不是传递category请使用CategoricalDtype的实例。
In [26]: from pandas.api.types import CategoricalDtypeIn [27]: s pd.Series([a, b, c, a])In [28]: cat_type CategoricalDtype(categories[b, c, d], orderedTrue)In [29]: s_cat s.astype(cat_type)In [30]: s_cat
Out[30]:
0 NaN
1 b
2 c
3 NaN
dtype: category
Categories (3, object): [b c d] 同样可以使用CategoricalDtype与DataFrame一起使用以确保所有列中的类别保持一致。
In [31]: from pandas.api.types import CategoricalDtypeIn [32]: df pd.DataFrame({A: list(abca), B: list(bccd)})In [33]: cat_type CategoricalDtype(categorieslist(abcd), orderedTrue)In [34]: df_cat df.astype(cat_type)In [35]: df_cat[A]
Out[35]:
0 a
1 b
2 c
3 a
Name: A, dtype: category
Categories (4, object): [a b c d]In [36]: df_cat[B]
Out[36]:
0 b
1 c
2 c
3 d
Name: B, dtype: category
Categories (4, object): [a b c d] 注意
要执行表格转换其中整个DataFrame中的所有标签都用作每列的类别可以通过categories pd.unique(df.to_numpy().ravel())来以编程方式确定categories参数。
如果您已经有codes和categories可以使用from_codes()构造函数在正常构造模式下保存因子化步骤
In [37]: splitter np.random.choice([0, 1], 5, p[0.5, 0.5])In [38]: s pd.Series(pd.Categorical.from_codes(splitter, categories[train, test])) 恢复原始数据
要恢复到原始的Series或 NumPy 数组请使用Series.astype(original_dtype)或np.asarray(categorical)
In [39]: s pd.Series([a, b, c, a])In [40]: s
Out[40]:
0 a
1 b
2 c
3 a
dtype: objectIn [41]: s2 s.astype(category)In [42]: s2
Out[42]:
0 a
1 b
2 c
3 a
dtype: category
Categories (3, object): [a, b, c]In [43]: s2.astype(str)
Out[43]:
0 a
1 b
2 c
3 a
dtype: objectIn [44]: np.asarray(s2)
Out[44]: array([a, b, c, a], dtypeobject) 注意
与 R 的factor函数相比分类数据不会将输入值转换为字符串类别最终将与原始值相同的数据类型。
注意
与 R 的factor函数相比目前没有办法在创建时分配/更改标签。在创建后使用categories来更改类别。
系列创建
可以通过几种方式创建DataFrame中的分类Series或列
在构造Series时指定dtypecategory
In [1]: s pd.Series([a, b, c, a], dtypecategory)In [2]: s
Out[2]:
0 a
1 b
2 c
3 a
dtype: category
Categories (3, object): [a, b, c] 通过将现有的Series或列转换为category dtype
In [3]: df pd.DataFrame({A: [a, b, c, a]})In [4]: df[B] df[A].astype(category)In [5]: df
Out[5]: A B
0 a a
1 b b
2 c c
3 a a 通过使用特殊函数例如cut()将数据分组为离散的箱。请参阅文档中有关切片的示例。
In [6]: df pd.DataFrame({value: np.random.randint(0, 100, 20)})In [7]: labels [{0} - {1}.format(i, i 9) for i in range(0, 100, 10)]In [8]: df[group] pd.cut(df.value, range(0, 105, 10), rightFalse, labelslabels)In [9]: df.head(10)
Out[9]: value group
0 65 60 - 69
1 49 40 - 49
2 56 50 - 59
3 43 40 - 49
4 43 40 - 49
5 91 90 - 99
6 32 30 - 39
7 87 80 - 89
8 36 30 - 39
9 8 0 - 9 通过将pandas.Categorical对象传递给Series或将其分配给DataFrame。
In [10]: raw_cat pd.Categorical(....: [a, b, c, a], categories[b, c, d], orderedFalse....: )....: In [11]: s pd.Series(raw_cat)In [12]: s
Out[12]:
0 NaN
1 b
2 c
3 NaN
dtype: category
Categories (3, object): [b, c, d]In [13]: df pd.DataFrame({A: [a, b, c, a]})In [14]: df[B] raw_catIn [15]: df
Out[15]: A B
0 a NaN
1 b b
2 c c
3 a NaN 分类数据具有特定的category dtype
In [16]: df.dtypes
Out[16]:
A object
B category
dtype: object DataFrame 创建
类似于前一节中将单个列转换为分类的情况DataFrame中的所有列可以在构建期间或构建后批量转换为分类。
可以在构建期间通过在DataFrame构造函数中指定dtypecategory来执行此操作
In [17]: df pd.DataFrame({A: list(abca), B: list(bccd)}, dtypecategory)In [18]: df.dtypes
Out[18]:
A category
B category
dtype: object 请注意每列中存在的类别不同转换是逐列进行的因此只有给定列中存在的标签才是类别
In [19]: df[A]
Out[19]:
0 a
1 b
2 c
3 a
Name: A, dtype: category
Categories (3, object): [a, b, c]In [20]: df[B]
Out[20]:
0 b
1 c
2 c
3 d
Name: B, dtype: category
Categories (3, object): [b, c, d] 类似地可以使用DataFrame.astype()来批量转换现有DataFrame中的所有列
In [21]: df pd.DataFrame({A: list(abca), B: list(bccd)})In [22]: df_cat df.astype(category)In [23]: df_cat.dtypes
Out[23]:
A category
B category
dtype: object 此转换也是逐列进行的
In [24]: df_cat[A]
Out[24]:
0 a
1 b
2 c
3 a
Name: A, dtype: category
Categories (3, object): [a, b, c]In [25]: df_cat[B]
Out[25]:
0 b
1 c
2 c
3 d
Name: B, dtype: category
Categories (3, object): [b, c, d] 控制行为
在上面的示例中我们传递了dtypecategory我们使用了默认行为 类别是从数据中推断出来的。 类别是无序的。
要控制这些行为而不是传递category请使用CategoricalDtype的实例。
In [26]: from pandas.api.types import CategoricalDtypeIn [27]: s pd.Series([a, b, c, a])In [28]: cat_type CategoricalDtype(categories[b, c, d], orderedTrue)In [29]: s_cat s.astype(cat_type)In [30]: s_cat
Out[30]:
0 NaN
1 b
2 c
3 NaN
dtype: category
Categories (3, object): [b c d] 同样可以在DataFrame中使用CategoricalDtype来确保所有列之间的类别保持一致。
In [31]: from pandas.api.types import CategoricalDtypeIn [32]: df pd.DataFrame({A: list(abca), B: list(bccd)})In [33]: cat_type CategoricalDtype(categorieslist(abcd), orderedTrue)In [34]: df_cat df.astype(cat_type)In [35]: df_cat[A]
Out[35]:
0 a
1 b
2 c
3 a
Name: A, dtype: category
Categories (4, object): [a b c d]In [36]: df_cat[B]
Out[36]:
0 b
1 c
2 c
3 d
Name: B, dtype: category
Categories (4, object): [a b c d] 注意
要执行表格转换其中整个DataFrame中的所有标签都用作每列的类别可以通过categories pd.unique(df.to_numpy().ravel())来以编程方式确定categories参数。
如果你已经有codes和categories可以使用from_codes()构造函数在正常构造模式下保存因子化步骤
In [37]: splitter np.random.choice([0, 1], 5, p[0.5, 0.5])In [38]: s pd.Series(pd.Categorical.from_codes(splitter, categories[train, test])) 恢复原始数据
要恢复到原始的Series或 NumPy 数组使用Series.astype(original_dtype)或np.asarray(categorical)
In [39]: s pd.Series([a, b, c, a])In [40]: s
Out[40]:
0 a
1 b
2 c
3 a
dtype: objectIn [41]: s2 s.astype(category)In [42]: s2
Out[42]:
0 a
1 b
2 c
3 a
dtype: category
Categories (3, object): [a, b, c]In [43]: s2.astype(str)
Out[43]:
0 a
1 b
2 c
3 a
dtype: objectIn [44]: np.asarray(s2)
Out[44]: array([a, b, c, a], dtypeobject) 注意
与 R 的factor函数相反分类数据不会将输入值转换为字符串类别最终将与原始值相同的数据类型。
注意
与 R 的factor函数相反目前没有办法在创建时分配/更改标签。在创建后使用categories更改类别。
CategoricalDtype
类别的类型完全由 categories一系列唯一值且没有缺失值 ordered一个布尔值
这些信息可以存储在CategoricalDtype中。categories参数是可选的这意味着实际的类别应该从创建pandas.Categorical时数据中推断出。默认情况下假定类别是无序的。
In [45]: from pandas.api.types import CategoricalDtypeIn [46]: CategoricalDtype([a, b, c])
Out[46]: CategoricalDtype(categories[a, b, c], orderedFalse, categories_dtypeobject)In [47]: CategoricalDtype([a, b, c], orderedTrue)
Out[47]: CategoricalDtype(categories[a, b, c], orderedTrue, categories_dtypeobject)In [48]: CategoricalDtype()
Out[48]: CategoricalDtype(categoriesNone, orderedFalse, categories_dtypeNone) CategoricalDtype可以在任何 pandas 期望dtype的地方使用。例如pandas.read_csv()pandas.DataFrame.astype()或在Series构造函数中。
注意
作为一种便利当你希望类别无序且等于数组中存在的值集时可以使用字符串category代替CategoricalDtype换句话说dtypecategory等同于dtypeCategoricalDtype()。
相等语义
两个CategoricalDtype实例具有相同的类别和顺序时它们比较相等。当比较两个无序的分类时categories的顺序不被考虑。
In [49]: c1 CategoricalDtype([a, b, c], orderedFalse)# Equal, since order is not considered when orderedFalse
In [50]: c1 CategoricalDtype([b, c, a], orderedFalse)
Out[50]: True# Unequal, since the second CategoricalDtype is ordered
In [51]: c1 CategoricalDtype([a, b, c], orderedTrue)
Out[51]: False 所有CategoricalDtype实例都与字符串category相等。
In [52]: c1 category
Out[52]: True 相等语义
两个CategoricalDtype实例具有相同的类别和顺序时它们比较相等。当比较两个无序的分类时categories的顺序不被考虑。
In [49]: c1 CategoricalDtype([a, b, c], orderedFalse)# Equal, since order is not considered when orderedFalse
In [50]: c1 CategoricalDtype([b, c, a], orderedFalse)
Out[50]: True# Unequal, since the second CategoricalDtype is ordered
In [51]: c1 CategoricalDtype([a, b, c], orderedTrue)
Out[51]: False 所有CategoricalDtype实例都与字符串category相等。
In [52]: c1 category
Out[52]: True 描述
在分类数据上使用describe()将产生类似于string类型的Series或DataFrame的输出。
In [53]: cat pd.Categorical([a, c, c, np.nan], categories[b, a, c])In [54]: df pd.DataFrame({cat: cat, s: [a, c, c, np.nan]})In [55]: df.describe()
Out[55]: cat s
count 3 3
unique 2 2
top c c
freq 2 2In [56]: df[cat].describe()
Out[56]:
count 3
unique 2
top c
freq 2
Name: cat, dtype: object 使用类别
分类数据有一个categories和一个ordered属性列出了它们可能的值以及排序是否重要。这些属性暴露为s.cat.categories和s.cat.ordered。如果您不手动指定类别和排序它们将从传递的参数中推断出来。
In [57]: s pd.Series([a, b, c, a], dtypecategory)In [58]: s.cat.categories
Out[58]: Index([a, b, c], dtypeobject)In [59]: s.cat.ordered
Out[59]: False 可以按特定顺序传递类别
In [60]: s pd.Series(pd.Categorical([a, b, c, a], categories[c, b, a]))In [61]: s.cat.categories
Out[61]: Index([c, b, a], dtypeobject)In [62]: s.cat.ordered
Out[62]: False 注意
新的分类数据不会自动排序。您必须显式传递orderedTrue来指示有序的Categorical。
注意
unique()的结果并不总是与Series.cat.categories相同因为Series.unique()有一些保证即它按出现顺序返回类别并且仅包括实际存在的值。
In [63]: s pd.Series(list(babc)).astype(CategoricalDtype(list(abcd)))In [64]: s
Out[64]:
0 b
1 a
2 b
3 c
dtype: category
Categories (4, object): [a, b, c, d]# categories
In [65]: s.cat.categories
Out[65]: Index([a, b, c, d], dtypeobject)# uniques
In [66]: s.unique()
Out[66]:
[b, a, c]
Categories (4, object): [a, b, c, d] 重命名类别
通过使用rename_categories()方法来重命名类别
In [67]: s pd.Series([a, b, c, a], dtypecategory)In [68]: s
Out[68]:
0 a
1 b
2 c
3 a
dtype: category
Categories (3, object): [a, b, c]In [69]: new_categories [Group %s % g for g in s.cat.categories]In [70]: s s.cat.rename_categories(new_categories)In [71]: s
Out[71]:
0 Group a
1 Group b
2 Group c
3 Group a
dtype: category
Categories (3, object): [Group a, Group b, Group c]# You can also pass a dict-like object to map the renaming
In [72]: s s.cat.rename_categories({1: x, 2: y, 3: z})In [73]: s
Out[73]:
0 Group a
1 Group b
2 Group c
3 Group a
dtype: category
Categories (3, object): [Group a, Group b, Group c] 注意
与 R 的factor相反分类数据可以具有其他类型的类别而不仅仅是字符串。
类别必须是唯一的否则会引发ValueError
In [74]: try:....: s s.cat.rename_categories([1, 1, 1])....: except ValueError as e:....: print(ValueError:, str(e))....:
ValueError: Categorical categories must be unique 类别也不能是NaN否则会引发ValueError
In [75]: try:....: s s.cat.rename_categories([1, 2, np.nan])....: except ValueError as e:....: print(ValueError:, str(e))....:
ValueError: Categorical categories cannot be null 添加新类别
可以使用add_categories()方法添加类别
In [76]: s s.cat.add_categories([4])In [77]: s.cat.categories
Out[77]: Index([Group a, Group b, Group c, 4], dtypeobject)In [78]: s
Out[78]:
0 Group a
1 Group b
2 Group c
3 Group a
dtype: category
Categories (4, object): [Group a, Group b, Group c, 4] 删除类别
可以使用remove_categories()方法删除类别。被删除的值将被np.nan替换。
In [79]: s s.cat.remove_categories([4])In [80]: s
Out[80]:
0 Group a
1 Group b
2 Group c
3 Group a
dtype: category
Categories (3, object): [Group a, Group b, Group c] 删除未使用的类别
也可以删除未使用的类别
In [81]: s pd.Series(pd.Categorical([a, b, a], categories[a, b, c, d]))In [82]: s
Out[82]:
0 a
1 b
2 a
dtype: category
Categories (4, object): [a, b, c, d]In [83]: s.cat.remove_unused_categories()
Out[83]:
0 a
1 b
2 a
dtype: category
Categories (2, object): [a, b] 设置类别
如果您想要一次性删除并添加新类别这样有一定的速度优势或者简单地将类别设置为预定义的范围请使用set_categories()。
In [84]: s pd.Series([one, two, four, -], dtypecategory)In [85]: s
Out[85]:
0 one
1 two
2 four
3 -
dtype: category
Categories (4, object): [-, four, one, two]In [86]: s s.cat.set_categories([one, two, three, four])In [87]: s
Out[87]:
0 one
1 two
2 four
3 NaN
dtype: category
Categories (4, object): [one, two, three, four] 注意
请注意Categorical.set_categories()无法知道某些类别是有意省略的还是因为拼写错误或在 Python3 下由于类型差异例如NumPy S1 dtype 和 Python 字符串。这可能导致意外行为
重命名类别
通过使用rename_categories()方法来重命名类别
In [67]: s pd.Series([a, b, c, a], dtypecategory)In [68]: s
Out[68]:
0 a
1 b
2 c
3 a
dtype: category
Categories (3, object): [a, b, c]In [69]: new_categories [Group %s % g for g in s.cat.categories]In [70]: s s.cat.rename_categories(new_categories)In [71]: s
Out[71]:
0 Group a
1 Group b
2 Group c
3 Group a
dtype: category
Categories (3, object): [Group a, Group b, Group c]# You can also pass a dict-like object to map the renaming
In [72]: s s.cat.rename_categories({1: x, 2: y, 3: z})In [73]: s
Out[73]:
0 Group a
1 Group b
2 Group c
3 Group a
dtype: category
Categories (3, object): [Group a, Group b, Group c] 注意
与 R 的factor相反分类数据可以具有其他类型的类别而不仅仅是字符串。
类别必须是唯一的否则会引发ValueError
In [74]: try:....: s s.cat.rename_categories([1, 1, 1])....: except ValueError as e:....: print(ValueError:, str(e))....:
ValueError: Categorical categories must be unique 类别也不能是NaN否则会引发ValueError
In [75]: try:....: s s.cat.rename_categories([1, 2, np.nan])....: except ValueError as e:....: print(ValueError:, str(e))....:
ValueError: Categorical categories cannot be null 添加新类别
可以使用add_categories()方法添加类别
In [76]: s s.cat.add_categories([4])In [77]: s.cat.categories
Out[77]: Index([Group a, Group b, Group c, 4], dtypeobject)In [78]: s
Out[78]:
0 Group a
1 Group b
2 Group c
3 Group a
dtype: category
Categories (4, object): [Group a, Group b, Group c, 4] 删除类别
可以使用remove_categories()方法删除类别。被删除的值将被np.nan替换。
In [79]: s s.cat.remove_categories([4])In [80]: s
Out[80]:
0 Group a
1 Group b
2 Group c
3 Group a
dtype: category
Categories (3, object): [Group a, Group b, Group c] 删除未使用的类别
也可以删除未使用的类别
In [81]: s pd.Series(pd.Categorical([a, b, a], categories[a, b, c, d]))In [82]: s
Out[82]:
0 a
1 b
2 a
dtype: category
Categories (4, object): [a, b, c, d]In [83]: s.cat.remove_unused_categories()
Out[83]:
0 a
1 b
2 a
dtype: category
Categories (2, object): [a, b] 设置类别
如果您想要一次性删除并添加新类别这样有一定的速度优势或者简单地将类别设置为预定义的范围请使用set_categories()。
In [84]: s pd.Series([one, two, four, -], dtypecategory)In [85]: s
Out[85]:
0 one
1 two
2 four
3 -
dtype: category
Categories (4, object): [-, four, one, two]In [86]: s s.cat.set_categories([one, two, three, four])In [87]: s
Out[87]:
0 one
1 two
2 four
3 NaN
dtype: category
Categories (4, object): [one, two, three, four] 注意
请注意Categorical.set_categories()无法知道某个类别是有意省略还是因为拼写错误或在 Python3 下由于类型差异例如NumPy S1 dtype 和 Python 字符串。这可能导致意外行为
排序和顺序
如果分类数据是有序的s.cat.ordered True那么类别的顺序具有意义并且可以进行某些操作。如果分类是无序的.min()/.max()会引发TypeError。
In [88]: s pd.Series(pd.Categorical([a, b, c, a], orderedFalse))In [89]: s s.sort_values()In [90]: s pd.Series([a, b, c, a]).astype(CategoricalDtype(orderedTrue))In [91]: s s.sort_values()In [92]: s
Out[92]:
0 a
3 a
1 b
2 c
dtype: category
Categories (3, object): [a b c]In [93]: s.min(), s.max()
Out[93]: (a, c) 您可以使用as_ordered()将分类数据设置为有序或使用as_unordered()将其设置为无序。这些默认情况下会返回一个新对象。
In [94]: s.cat.as_ordered()
Out[94]:
0 a
3 a
1 b
2 c
dtype: category
Categories (3, object): [a b c]In [95]: s.cat.as_unordered()
Out[95]:
0 a
3 a
1 b
2 c
dtype: category
Categories (3, object): [a, b, c] 排序将使用由categories定义的顺序而不是数据类型上存在的任何词法顺序。即使对于字符串和数字数据也是如此
In [96]: s pd.Series([1, 2, 3, 1], dtypecategory)In [97]: s s.cat.set_categories([2, 3, 1], orderedTrue)In [98]: s
Out[98]:
0 1
1 2
2 3
3 1
dtype: category
Categories (3, int64): [2 3 1]In [99]: s s.sort_values()In [100]: s
Out[100]:
1 2
2 3
0 1
3 1
dtype: category
Categories (3, int64): [2 3 1]In [101]: s.min(), s.max()
Out[101]: (2, 1) 重新排序
通过Categorical.reorder_categories()和Categorical.set_categories()方法可以重新排序类别。对于Categorical.reorder_categories()所有旧类别必须包含在新类别中不允许有新类别。这将必然使排序顺序与类别顺序相同。
In [102]: s pd.Series([1, 2, 3, 1], dtypecategory)In [103]: s s.cat.reorder_categories([2, 3, 1], orderedTrue)In [104]: s
Out[104]:
0 1
1 2
2 3
3 1
dtype: category
Categories (3, int64): [2 3 1]In [105]: s s.sort_values()In [106]: s
Out[106]:
1 2
2 3
0 1
3 1
dtype: category
Categories (3, int64): [2 3 1]In [107]: s.min(), s.max()
Out[107]: (2, 1) 注意
注意在分配新类别和重新排序类别之间的区别第一个重命名类别因此Series中的个别值也会被重命名但如果第一个位置被排序到最后重命名的值仍将被排序到最后。重新排序意味着值排序的方式之后不同但不意味着Series中的个别值被更改。
注意
如果Categorical未排序Series.min()和Series.max()会引发TypeError。像、-、*、/和基于它们的操作例如Series.median()如果数组的长度是偶数则需要计算两个值之间的平均值这样的数值操作不起作用并引发TypeError。
多列排序
一个分类数据类型的列将以与其他列类似的方式参与多列排序。分类的排序由该列的categories确定。
In [108]: dfs pd.DataFrame(.....: {.....: A: pd.Categorical(.....: list(bbeebbaa),.....: categories[e, a, b],.....: orderedTrue,.....: ),.....: B: [1, 2, 1, 2, 2, 1, 2, 1],.....: }.....: ).....: In [109]: dfs.sort_values(by[A, B])
Out[109]: A B
2 e 1
3 e 2
7 a 1
6 a 2
0 b 1
5 b 1
1 b 2
4 b 2 重新排序categories会改变未来的排序。
In [110]: dfs[A] dfs[A].cat.reorder_categories([a, b, e])In [111]: dfs.sort_values(by[A, B])
Out[111]: A B
7 a 1
6 a 2
0 b 1
5 b 1
1 b 2
4 b 2
2 e 1
3 e 2 重新排序
通过Categorical.reorder_categories()和Categorical.set_categories()方法可以重新排序类别。对于Categorical.reorder_categories()所有旧类别必须包含在新类别中不允许有新类别。这将必然使排序顺序与类别顺序相同。
In [102]: s pd.Series([1, 2, 3, 1], dtypecategory)In [103]: s s.cat.reorder_categories([2, 3, 1], orderedTrue)In [104]: s
Out[104]:
0 1
1 2
2 3
3 1
dtype: category
Categories (3, int64): [2 3 1]In [105]: s s.sort_values()In [106]: s
Out[106]:
1 2
2 3
0 1
3 1
dtype: category
Categories (3, int64): [2 3 1]In [107]: s.min(), s.max()
Out[107]: (2, 1) 注意
注意分配新类别和重新排序类别之间的区别第一个重命名类别因此Series中的个别值也会被重命名但如果第一个位置被排序为最后一个则重命名的值仍将被排序为最后一个。重新排序意味着排序值的方式在之后不同但不意味着Series中的个别值被更改。
注意
如果Categorical未排序Series.min()和Series.max()将引发TypeError。像、-、*、/和基于它们的操作例如Series.median()如果数组的长度是偶数则需要计算两个值之间的平均值的数值操作也不起作用会引发TypeError。
多列排序
分类数据类型的列将以与其他列类似的方式参与多列排序。分类的排序由该列的categories确定。
In [108]: dfs pd.DataFrame(.....: {.....: A: pd.Categorical(.....: list(bbeebbaa),.....: categories[e, a, b],.....: orderedTrue,.....: ),.....: B: [1, 2, 1, 2, 2, 1, 2, 1],.....: }.....: ).....: In [109]: dfs.sort_values(by[A, B])
Out[109]: A B
2 e 1
3 e 2
7 a 1
6 a 2
0 b 1
5 b 1
1 b 2
4 b 2 重新排序categories会改变未来的排序。
In [110]: dfs[A] dfs[A].cat.reorder_categories([a, b, e])In [111]: dfs.sort_values(by[A, B])
Out[111]: A B
7 a 1
6 a 2
0 b 1
5 b 1
1 b 2
4 b 2
2 e 1
3 e 2 比较
将分类数据与其他对象进行比较有三种情况 将等号(和!)与与分类数据长度相同的列表对象列表、Series、数组等进行比较。 所有对另一个分类系列进行比较、!、、、和当orderedTrue且categories相同时。 所有对分类数据与标量的比较。
所有其他比较特别是两个具有不同类别的分类或分类与任何类似列表对象的“非相等”比较都会引发TypeError。
注意
任何对分类数据与Series、np.array、list或具有不同类别或排序的分类数据进行“非相等”比较都会引发TypeError因为自定义类别排序可能会被解释为两种方式一种考虑排序一种不考虑。
In [112]: cat pd.Series([1, 2, 3]).astype(CategoricalDtype([3, 2, 1], orderedTrue))In [113]: cat_base pd.Series([2, 2, 2]).astype(CategoricalDtype([3, 2, 1], orderedTrue))In [114]: cat_base2 pd.Series([2, 2, 2]).astype(CategoricalDtype(orderedTrue))In [115]: cat
Out[115]:
0 1
1 2
2 3
dtype: category
Categories (3, int64): [3 2 1]In [116]: cat_base
Out[116]:
0 2
1 2
2 2
dtype: category
Categories (3, int64): [3 2 1]In [117]: cat_base2
Out[117]:
0 2
1 2
2 2
dtype: category
Categories (1, int64): [2] 与具有相同类别和排序或标量的分类进行比较有效
In [118]: cat cat_base
Out[118]:
0 True
1 False
2 False
dtype: boolIn [119]: cat 2
Out[119]:
0 True
1 False
2 False
dtype: bool 相等比较适用于任何长度相同的类似列表对象和标量
In [120]: cat cat_base
Out[120]:
0 False
1 True
2 False
dtype: boolIn [121]: cat np.array([1, 2, 3])
Out[121]:
0 True
1 True
2 True
dtype: boolIn [122]: cat 2
Out[122]:
0 False
1 True
2 False
dtype: bool 这不起作用因为类别不同
In [123]: try:.....: cat cat_base2.....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Categoricals can only be compared if categories are the same. 如果要对分类系列与非分类数据的类似列表对象进行“非相等”比较需要明确并将分类数据转换回原始值
In [124]: base np.array([1, 2, 3])In [125]: try:.....: cat base.....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Cannot compare a Categorical for op __gt__ with type class numpy.ndarray.
If you want to compare values, use np.asarray(cat) op other.In [126]: np.asarray(cat) base
Out[126]: array([False, False, False]) 当您比较具有相同类别的无序分类时不考虑顺序
In [127]: c1 pd.Categorical([a, b], categories[a, b], orderedFalse)In [128]: c2 pd.Categorical([a, b], categories[b, a], orderedFalse)In [129]: c1 c2
Out[129]: array([ True, True]) 操作
除了Series.min()、Series.max()和Series.mode()之外还可以对分类数据进行以下操作
Series方法如Series.value_counts()将使用所有类别即使某些类别在数据中不存在
In [130]: s pd.Series(pd.Categorical([a, b, c, c], categories[c, a, b, d]))In [131]: s.value_counts()
Out[131]:
c 2
a 1
b 1
d 0
Name: count, dtype: int64 DataFrame方法如DataFrame.sum()在observedFalse时也会显示“未使用”的类别。
In [132]: columns pd.Categorical(.....: [One, One, Two], categories[One, Two, Three], orderedTrue.....: ).....: In [133]: df pd.DataFrame(.....: data[[1, 2, 3], [4, 5, 6]],.....: columnspd.MultiIndex.from_arrays([[A, B, B], columns]),.....: ).T.....: In [134]: df.groupby(level1, observedFalse).sum()
Out[134]: 0 1
One 3 9
Two 3 6
Three 0 0 当observedFalse时Groupby 也会显示“未使用”的类别
In [135]: cats pd.Categorical(.....: [a, b, b, b, c, c, c], categories[a, b, c, d].....: ).....: In [136]: df pd.DataFrame({cats: cats, values: [1, 2, 2, 2, 3, 4, 5]})In [137]: df.groupby(cats, observedFalse).mean()
Out[137]: values
cats
a 1.0
b 2.0
c 4.0
d NaNIn [138]: cats2 pd.Categorical([a, a, b, b], categories[a, b, c])In [139]: df2 pd.DataFrame(.....: {.....: cats: cats2,.....: B: [c, d, c, d],.....: values: [1, 2, 3, 4],.....: }.....: ).....: In [140]: df2.groupby([cats, B], observedFalse).mean()
Out[140]: values
cats B
a c 1.0d 2.0
b c 3.0d 4.0
c c NaNd NaN 透视表
In [141]: raw_cat pd.Categorical([a, a, b, b], categories[a, b, c])In [142]: df pd.DataFrame({A: raw_cat, B: [c, d, c, d], values: [1, 2, 3, 4]})In [143]: pd.pivot_table(df, valuesvalues, index[A, B], observedFalse)
Out[143]: values
A B
a c 1.0d 2.0
b c 3.0d 4.0 数据整理
优化过的 pandas 数据访问方法.loc、.iloc、.at和.iat的工作方式与正常情况下相同。唯一的区别是返回类型用于获取和只有已在categories中的值才能被赋值。
获取
如果切片操作返回DataFrame或类型为Series的列则category dtype 将被保留。
In [144]: idx pd.Index([h, i, j, k, l, m, n])In [145]: cats pd.Series([a, b, b, b, c, c, c], dtypecategory, indexidx)In [146]: values [1, 2, 2, 2, 3, 4, 5]In [147]: df pd.DataFrame({cats: cats, values: values}, indexidx)In [148]: df.iloc[2:4, :]
Out[148]: cats values
j b 2
k b 2In [149]: df.iloc[2:4, :].dtypes
Out[149]:
cats category
values int64
dtype: objectIn [150]: df.loc[h:j, cats]
Out[150]:
h a
i b
j b
Name: cats, dtype: category
Categories (3, object): [a, b, c]In [151]: df[df[cats] b]
Out[151]: cats values
i b 2
j b 2
k b 2 如果您只取一行则类别类型不会被保留的示例结果的Series的 dtype 为object
# get the complete h row as a Series
In [152]: df.loc[h, :]
Out[152]:
cats a
values 1
Name: h, dtype: object 从分类数据中返回单个项目也将返回该值而不是长度为“1”的分类。
In [153]: df.iat[0, 0]
Out[153]: aIn [154]: df[cats] df[cats].cat.rename_categories([x, y, z])In [155]: df.at[h, cats] # returns a string
Out[155]: x 注意
这与 R 的factor函数形成对比其中factor(c(1,2,3))[1]返回一个单一值factor。
要获取类型为category的单个值Series您需要传入一个包含单个值的列表
In [156]: df.loc[[h], cats]
Out[156]:
h x
Name: cats, dtype: category
Categories (3, object): [x, y, z] 字符串和日期时间访问器
如果s.cat.categories的访问器.dt和.str是适当类型则会起作用
In [157]: str_s pd.Series(list(aabb))In [158]: str_cat str_s.astype(category)In [159]: str_cat
Out[159]:
0 a
1 a
2 b
3 b
dtype: category
Categories (2, object): [a, b]In [160]: str_cat.str.contains(a)
Out[160]:
0 True
1 True
2 False
3 False
dtype: boolIn [161]: date_s pd.Series(pd.date_range(1/1/2015, periods5))In [162]: date_cat date_s.astype(category)In [163]: date_cat
Out[163]:
0 2015-01-01
1 2015-01-02
2 2015-01-03
3 2015-01-04
4 2015-01-05
dtype: category
Categories (5, datetime64[ns]): [2015-01-01, 2015-01-02, 2015-01-03, 2015-01-04, 2015-01-05]In [164]: date_cat.dt.day
Out[164]:
0 1
1 2
2 3
3 4
4 5
dtype: int32 注意
返回的Series或DataFrame与如果您在该类型的Series上使用.str.method / .dt.method时的类型相同而不是category类型。
这意味着从Series的访问器上的方法和属性返回的值以及将这个Series转换为category类型后的访问器上的方法和属性返回的值将是相等的
In [165]: ret_s str_s.str.contains(a)In [166]: ret_cat str_cat.str.contains(a)In [167]: ret_s.dtype ret_cat.dtype
Out[167]: TrueIn [168]: ret_s ret_cat
Out[168]:
0 True
1 True
2 True
3 True
dtype: bool 注意
工作是在categories上进行的然后构建一个新的Series。如果您有一个类型为字符串的Series其中许多元素重复即Series中的唯一元素数量远小于Series的长度这会对性能产生一些影响。在这种情况下将原始Series转换为category类型并在其上使.str.method或.dt.property可能更快。
设置
在分类列或Series中设置值只要该值包含在categories中即可
In [169]: idx pd.Index([h, i, j, k, l, m, n])In [170]: cats pd.Categorical([a, a, a, a, a, a, a], categories[a, b])In [171]: values [1, 1, 1, 1, 1, 1, 1]In [172]: df pd.DataFrame({cats: cats, values: values}, indexidx)In [173]: df.iloc[2:4, :] [[b, 2], [b, 2]]In [174]: df
Out[174]: cats values
h a 1
i a 1
j b 2
k b 2
l a 1
m a 1
n a 1In [175]: try:.....: df.iloc[2:4, :] [[c, 3], [c, 3]].....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Cannot setitem on a Categorical with a new category, set the categories first 通过分配分类数据来设置值还将检查categories是否匹配
In [176]: df.loc[j:k, cats] pd.Categorical([a, a], categories[a, b])In [177]: df
Out[177]: cats values
h a 1
i a 1
j a 2
k a 2
l a 1
m a 1
n a 1In [178]: try:.....: df.loc[j:k, cats] pd.Categorical([b, b], categories[a, b, c]).....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Cannot set a Categorical with another, without identical categories 将Categorical分配给其他类型列的部分将使用这些值
In [179]: df pd.DataFrame({a: [1, 1, 1, 1, 1], b: [a, a, a, a, a]})In [180]: df.loc[1:2, a] pd.Categorical([b, b], categories[a, b])In [181]: df.loc[2:3, b] pd.Categorical([b, b], categories[a, b])In [182]: df
Out[182]: a b
0 1 a
1 b a
2 b b
3 1 b
4 1 aIn [183]: df.dtypes
Out[183]:
a object
b object
dtype: object 合并/连接
默认情况下合并包含相同类别的Series或DataFrames将导致category类型否则结果将取决于底层类别的类型。导致非分类类型的合并可能会导致更高的内存使用量。使用.astype或union_categoricals来确保category类型的结果。
In [184]: from pandas.api.types import union_categoricals# same categories
In [185]: s1 pd.Series([a, b], dtypecategory)In [186]: s2 pd.Series([a, b, a], dtypecategory)In [187]: pd.concat([s1, s2])
Out[187]:
0 a
1 b
0 a
1 b
2 a
dtype: category
Categories (2, object): [a, b]# different categories
In [188]: s3 pd.Series([b, c], dtypecategory)In [189]: pd.concat([s1, s3])
Out[189]:
0 a
1 b
0 b
1 c
dtype: object# Output dtype is inferred based on categories values
In [190]: int_cats pd.Series([1, 2], dtypecategory)In [191]: float_cats pd.Series([3.0, 4.0], dtypecategory)In [192]: pd.concat([int_cats, float_cats])
Out[192]:
0 1.0
1 2.0
0 3.0
1 4.0
dtype: float64In [193]: pd.concat([s1, s3]).astype(category)
Out[193]:
0 a
1 b
0 b
1 c
dtype: category
Categories (3, object): [a, b, c]In [194]: union_categoricals([s1.array, s3.array])
Out[194]:
[a, b, b, c]
Categories (3, object): [a, b, c] 以下表总结了合并Categoricals的结果
arg1arg2相同结果categorycategoryTruecategorycategory (object)category (object)Falseobject (dtype is inferred)
| category (int) | category (float) | False | float (dtype is inferred) | ### Unioning
如果要合并不一定具有相同类别的分类变量union_categoricals()函数将合并类别的列表。新的类别将是被合并类别的并集。
In [195]: from pandas.api.types import union_categoricalsIn [196]: a pd.Categorical([b, c])In [197]: b pd.Categorical([a, b])In [198]: union_categoricals([a, b])
Out[198]:
[b, c, a, b]
Categories (3, object): [b, c, a] 默认情况下结果类别将按照它们在数据中出现的顺序排序。如果希望类别按字典序排序请使用sort_categoriesTrue参数。
In [199]: union_categoricals([a, b], sort_categoriesTrue)
Out[199]:
[b, c, a, b]
Categories (3, object): [a, b, c] union_categoricals也适用于“简单”情况即合并具有相同类别和顺序信息的两个分类变量例如您也可以使用append。
In [200]: a pd.Categorical([a, b], orderedTrue)In [201]: b pd.Categorical([a, b, a], orderedTrue)In [202]: union_categoricals([a, b])
Out[202]:
[a, b, a, b, a]
Categories (2, object): [a b] 以下代码会引发TypeError因为类别是有序的而且不相同。
In [203]: a pd.Categorical([a, b], orderedTrue)In [204]: b pd.Categorical([a, b, c], orderedTrue)In [205]: union_categoricals([a, b])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[205], line 1
---- 1 union_categoricals([a, b])File ~/work/pandas/pandas/pandas/core/dtypes/concat.py:341, in union_categoricals(to_union, sort_categories, ignore_order)339 if all(c.ordered for c in to_union):340 msg to union ordered Categoricals, all categories must be the same
-- 341 raise TypeError(msg)342 raise TypeError(Categorical.ordered must be the same)344 if ignore_order:TypeError: to union ordered Categoricals, all categories must be the same 使用ignore_orderedTrue参数可以合并具有不同类别或排序的有序分类变量。
In [206]: a pd.Categorical([a, b, c], orderedTrue)In [207]: b pd.Categorical([c, b, a], orderedTrue)In [208]: union_categoricals([a, b], ignore_orderTrue)
Out[208]:
[a, b, c, c, b, a]
Categories (3, object): [a, b, c] union_categoricals()也适用于CategoricalIndex或包含分类数据的Series但请注意结果数组将始终是普通的Categorical
In [209]: a pd.Series([b, c], dtypecategory)In [210]: b pd.Series([a, b], dtypecategory)In [211]: union_categoricals([a, b])
Out[211]:
[b, c, a, b]
Categories (3, object): [b, c, a] 注意
当合并分类变量时union_categoricals可能会重新编码类别的整数编码。这可能是您想要的但如果您依赖于类别的确切编号请注意。
In [212]: c1 pd.Categorical([b, c])In [213]: c2 pd.Categorical([a, b])In [214]: c1
Out[214]:
[b, c]
Categories (2, object): [b, c]# b is coded to 0
In [215]: c1.codes
Out[215]: array([0, 1], dtypeint8)In [216]: c2
Out[216]:
[a, b]
Categories (2, object): [a, b]# b is coded to 1
In [217]: c2.codes
Out[217]: array([0, 1], dtypeint8)In [218]: c union_categoricals([c1, c2])In [219]: c
Out[219]:
[b, c, a, b]
Categories (3, object): [b, c, a]# b is coded to 0 throughout, same as c1, different from c2
In [220]: c.codes
Out[220]: array([0, 1, 2, 0], dtypeint8) 获取
如果切片操作返回DataFrame或Series类型的列category类型将被保留。
In [144]: idx pd.Index([h, i, j, k, l, m, n])In [145]: cats pd.Series([a, b, b, b, c, c, c], dtypecategory, indexidx)In [146]: values [1, 2, 2, 2, 3, 4, 5]In [147]: df pd.DataFrame({cats: cats, values: values}, indexidx)In [148]: df.iloc[2:4, :]
Out[148]: cats values
j b 2
k b 2In [149]: df.iloc[2:4, :].dtypes
Out[149]:
cats category
values int64
dtype: objectIn [150]: df.loc[h:j, cats]
Out[150]:
h a
i b
j b
Name: cats, dtype: category
Categories (3, object): [a, b, c]In [151]: df[df[cats] b]
Out[151]: cats values
i b 2
j b 2
k b 2 如果您只取一行作为示例类别类型可能不会被保留结果的Series类型为object
# get the complete h row as a Series
In [152]: df.loc[h, :]
Out[152]:
cats a
values 1
Name: h, dtype: object 从分类数据中返回单个项目也将返回该值而不是长度为“1”的分类。
In [153]: df.iat[0, 0]
Out[153]: aIn [154]: df[cats] df[cats].cat.rename_categories([x, y, z])In [155]: df.at[h, cats] # returns a string
Out[155]: x 注意
这与 R 的factor函数形成对比其中factor(c(1,2,3))[1]返回一个单一值factor。
要获得类型为category的单一值Series您可以传入一个只有一个值的列表
In [156]: df.loc[[h], cats]
Out[156]:
h x
Name: cats, dtype: category
Categories (3, object): [x, y, z] 字符串和日期时间访问器
如果s.cat.categories的类型适当访问器.dt和.str将起作用
In [157]: str_s pd.Series(list(aabb))In [158]: str_cat str_s.astype(category)In [159]: str_cat
Out[159]:
0 a
1 a
2 b
3 b
dtype: category
Categories (2, object): [a, b]In [160]: str_cat.str.contains(a)
Out[160]:
0 True
1 True
2 False
3 False
dtype: boolIn [161]: date_s pd.Series(pd.date_range(1/1/2015, periods5))In [162]: date_cat date_s.astype(category)In [163]: date_cat
Out[163]:
0 2015-01-01
1 2015-01-02
2 2015-01-03
3 2015-01-04
4 2015-01-05
dtype: category
Categories (5, datetime64[ns]): [2015-01-01, 2015-01-02, 2015-01-03, 2015-01-04, 2015-01-05]In [164]: date_cat.dt.day
Out[164]:
0 1
1 2
2 3
3 4
4 5
dtype: int32 注意
返回的Series或DataFrame与在该类型的Series上使用.str.method / .dt.method时的类型相同而不是category类型。
这意味着从Series的访问器的方法和属性返回的值以及将这个Series转换为category类型后从其访问器的方法和属性返回的值将是相等的
In [165]: ret_s str_s.str.contains(a)In [166]: ret_cat str_cat.str.contains(a)In [167]: ret_s.dtype ret_cat.dtype
Out[167]: TrueIn [168]: ret_s ret_cat
Out[168]:
0 True
1 True
2 True
3 True
dtype: bool 注意
工作是在categories上进行的然后构建一个新的Series。如果您有一个字符串类型的Series其中有很多重复的元素即Series中唯一元素的数量远小于Series的长度这会对性能产生一些影响。在这种情况下将原始Series转换为category类型并在其上使用.str.method或.dt.property可能更快。
设置
在分类列或Series中设置值只要该值包含在categories中即可
In [169]: idx pd.Index([h, i, j, k, l, m, n])In [170]: cats pd.Categorical([a, a, a, a, a, a, a], categories[a, b])In [171]: values [1, 1, 1, 1, 1, 1, 1]In [172]: df pd.DataFrame({cats: cats, values: values}, indexidx)In [173]: df.iloc[2:4, :] [[b, 2], [b, 2]]In [174]: df
Out[174]: cats values
h a 1
i a 1
j b 2
k b 2
l a 1
m a 1
n a 1In [175]: try:.....: df.iloc[2:4, :] [[c, 3], [c, 3]].....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Cannot setitem on a Categorical with a new category, set the categories first 通过分配分类数据来设置值也会检查categories是否匹配
In [176]: df.loc[j:k, cats] pd.Categorical([a, a], categories[a, b])In [177]: df
Out[177]: cats values
h a 1
i a 1
j a 2
k a 2
l a 1
m a 1
n a 1In [178]: try:.....: df.loc[j:k, cats] pd.Categorical([b, b], categories[a, b, c]).....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Cannot set a Categorical with another, without identical categories 将Categorical分配给其他类型列的部分将使用这些值
In [179]: df pd.DataFrame({a: [1, 1, 1, 1, 1], b: [a, a, a, a, a]})In [180]: df.loc[1:2, a] pd.Categorical([b, b], categories[a, b])In [181]: df.loc[2:3, b] pd.Categorical([b, b], categories[a, b])In [182]: df
Out[182]: a b
0 1 a
1 b a
2 b b
3 1 b
4 1 aIn [183]: df.dtypes
Out[183]:
a object
b object
dtype: object 合并/连接
默认情况下合并包含相同类别的Series或DataFrames将导致category数据类型否则结果将取决于底层类别的数据类型。导致非分类数据类型的合并可能会导致更高的内存使用量。使用.astype或union_categoricals来确保获得category结果。
In [184]: from pandas.api.types import union_categoricals# same categories
In [185]: s1 pd.Series([a, b], dtypecategory)In [186]: s2 pd.Series([a, b, a], dtypecategory)In [187]: pd.concat([s1, s2])
Out[187]:
0 a
1 b
0 a
1 b
2 a
dtype: category
Categories (2, object): [a, b]# different categories
In [188]: s3 pd.Series([b, c], dtypecategory)In [189]: pd.concat([s1, s3])
Out[189]:
0 a
1 b
0 b
1 c
dtype: object# Output dtype is inferred based on categories values
In [190]: int_cats pd.Series([1, 2], dtypecategory)In [191]: float_cats pd.Series([3.0, 4.0], dtypecategory)In [192]: pd.concat([int_cats, float_cats])
Out[192]:
0 1.0
1 2.0
0 3.0
1 4.0
dtype: float64In [193]: pd.concat([s1, s3]).astype(category)
Out[193]:
0 a
1 b
0 b
1 c
dtype: category
Categories (3, object): [a, b, c]In [194]: union_categoricals([s1.array, s3.array])
Out[194]:
[a, b, b, c]
Categories (3, object): [a, b, c] 以下表总结了合并Categoricals的结果
arg1arg2相同结果类别类别True类别类别object类别objectFalseobject数据类型被推断类别int类别floatFalsefloat数据类型被推断
联合
如果要组合不一定具有相同类别的分类变量则union_categoricals() 函数将组合一个类别列表。新的类别将是被组合的类别的并集。
In [195]: from pandas.api.types import union_categoricalsIn [196]: a pd.Categorical([b, c])In [197]: b pd.Categorical([a, b])In [198]: union_categoricals([a, b])
Out[198]:
[b, c, a, b]
Categories (3, object): [b, c, a] 默认情况下结果类别将按照它们在数据中出现的顺序排序。如果希望类别按字典顺序排序请使用sort_categoriesTrue参数。
In [199]: union_categoricals([a, b], sort_categoriesTrue)
Out[199]:
[b, c, a, b]
Categories (3, object): [a, b, c] union_categoricals 也适用于“简单”情况即组合具有相同类别和排序信息的两个分类变量例如您也可以使用append。
In [200]: a pd.Categorical([a, b], orderedTrue)In [201]: b pd.Categorical([a, b, a], orderedTrue)In [202]: union_categoricals([a, b])
Out[202]:
[a, b, a, b, a]
Categories (2, object): [a b] 以下代码会引发TypeError因为类别是有序的且不相同。
In [203]: a pd.Categorical([a, b], orderedTrue)In [204]: b pd.Categorical([a, b, c], orderedTrue)In [205]: union_categoricals([a, b])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[205], line 1
---- 1 union_categoricals([a, b])File ~/work/pandas/pandas/pandas/core/dtypes/concat.py:341, in union_categoricals(to_union, sort_categories, ignore_order)339 if all(c.ordered for c in to_union):340 msg to union ordered Categoricals, all categories must be the same
-- 341 raise TypeError(msg)342 raise TypeError(Categorical.ordered must be the same)344 if ignore_order:TypeError: to union ordered Categoricals, all categories must be the same 可以通过使用ignore_orderedTrue参数来组合具有不同类别或排序的有序分类。
In [206]: a pd.Categorical([a, b, c], orderedTrue)In [207]: b pd.Categorical([c, b, a], orderedTrue)In [208]: union_categoricals([a, b], ignore_orderTrue)
Out[208]:
[a, b, c, c, b, a]
Categories (3, object): [a, b, c] union_categoricals() 也适用于CategoricalIndex或包含分类数据的Series但请注意结果数组将始终是普通的Categorical
In [209]: a pd.Series([b, c], dtypecategory)In [210]: b pd.Series([a, b], dtypecategory)In [211]: union_categoricals([a, b])
Out[211]:
[b, c, a, b]
Categories (3, object): [b, c, a] 注意
当组合分类数据时union_categoricals可能会重新编码类别的整数代码。这可能是您想要的但如果依赖于类别的确切编号请注意。
In [212]: c1 pd.Categorical([b, c])In [213]: c2 pd.Categorical([a, b])In [214]: c1
Out[214]:
[b, c]
Categories (2, object): [b, c]# b is coded to 0
In [215]: c1.codes
Out[215]: array([0, 1], dtypeint8)In [216]: c2
Out[216]:
[a, b]
Categories (2, object): [a, b]# b is coded to 1
In [217]: c2.codes
Out[217]: array([0, 1], dtypeint8)In [218]: c union_categoricals([c1, c2])In [219]: c
Out[219]:
[b, c, a, b]
Categories (3, object): [b, c, a]# b is coded to 0 throughout, same as c1, different from c2
In [220]: c.codes
Out[220]: array([0, 1, 2, 0], dtypeint8) 数据的读取/写入
您可以将包含category dtypes 的数据写入HDFStore。参见这里以获取示例和注意事项。
也可以将数据写入和从Stata格式文件中读取。参见这里以获取示例和注意事项。
写入 CSV 文件将转换数据实际上删除有关分类类别和排序的任何信息。因此如果您读取 CSV 文件必须将相关列转换回category并分配正确的类别和类别排序。
In [221]: import ioIn [222]: s pd.Series(pd.Categorical([a, b, b, a, a, d]))# rename the categories
In [223]: s s.cat.rename_categories([very good, good, bad])# reorder the categories and add missing categories
In [224]: s s.cat.set_categories([very bad, bad, medium, good, very good])In [225]: df pd.DataFrame({cats: s, vals: [1, 2, 3, 4, 5, 6]})In [226]: csv io.StringIO()In [227]: df.to_csv(csv)In [228]: df2 pd.read_csv(io.StringIO(csv.getvalue()))In [229]: df2.dtypes
Out[229]:
Unnamed: 0 int64
cats object
vals int64
dtype: objectIn [230]: df2[cats]
Out[230]:
0 very good
1 good
2 good
3 very good
4 very good
5 bad
Name: cats, dtype: object# Redo the category
In [231]: df2[cats] df2[cats].astype(category)In [232]: df2[cats] df2[cats].cat.set_categories(.....: [very bad, bad, medium, good, very good].....: ).....: In [233]: df2.dtypes
Out[233]:
Unnamed: 0 int64
cats category
vals int64
dtype: objectIn [234]: df2[cats]
Out[234]:
0 very good
1 good
2 good
3 very good
4 very good
5 bad
Name: cats, dtype: category
Categories (5, object): [very bad, bad, medium, good, very good] 使用to_sql将数据写入 SQL 数据时也是如此。
缺失数据
pandas 主要使用数值np.nan来表示缺失数据。默认情况下不包括在计算中。参见缺失数据部分。
缺失值不应包括在分类categories中只应包括在values中。相反应理解 NaN 是不同的并且始终可能存在。在处理分类codes时缺失值将始终具有代码-1。
In [235]: s pd.Series([a, b, np.nan, a], dtypecategory)# only two categories
In [236]: s
Out[236]:
0 a
1 b
2 NaN
3 a
dtype: category
Categories (2, object): [a, b]In [237]: s.cat.codes
Out[237]:
0 0
1 1
2 -1
3 0
dtype: int8 处理缺失数据的方法例如isna()fillna()dropna()都可以正常工作
In [238]: s pd.Series([a, b, np.nan], dtypecategory)In [239]: s
Out[239]:
0 a
1 b
2 NaN
dtype: category
Categories (2, object): [a, b]In [240]: pd.isna(s)
Out[240]:
0 False
1 False
2 True
dtype: boolIn [241]: s.fillna(a)
Out[241]:
0 a
1 b
2 a
dtype: category
Categories (2, object): [a, b] 与 R 的factor的差异
以下与 R 的因子函数的差异可以观察到 R 的levels被命名为categories。 R 的levels始终为字符串类型而 pandas 中的categories可以是任何 dtype。 不可能在创建时指定标签。之后使用s.cat.rename_categories(new_labels)。 与 R 的factor函数相反将分类数据作为创建新分类系列的唯一输入将不会删除未使用的类别而是创建一个等于传入的新分类系列 R 允许在其levelspandas 的categories中包含缺失值。pandas 不允许NaN类别但缺失值仍然可以在values中。
注意事项
内存使用
Categorical的内存使用量与类别数和数据长度成正比。相比之下object dtype 是数据长度的常数倍。
In [242]: s pd.Series([foo, bar] * 1000)# object dtype
In [243]: s.nbytes
Out[243]: 16000# category dtype
In [244]: s.astype(category).nbytes
Out[244]: 2016 注意
如果类别数接近数据长度Categorical将使用几乎相同或更多的内存而不是等效的object dtype 表示。
In [245]: s pd.Series([foo%04d % i for i in range(2000)])# object dtype
In [246]: s.nbytes
Out[246]: 16000# category dtype
In [247]: s.astype(category).nbytes
Out[247]: 20000 Categorical不是numpy数组
目前分类数据和底层的Categorical是作为 Python 对象实现的而不是作为低级别的 NumPy 数组 dtype。这会导致一些问题。
NumPy 本身不知道新的 dtype
In [248]: try:.....: np.dtype(category).....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: data type category not understoodIn [249]: dtype pd.Categorical([a]).dtypeIn [250]: try:.....: np.dtype(dtype).....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Cannot interpret CategoricalDtype(categories[a], orderedFalse, categories_dtypeobject) as a data type Dtype 比较有效
In [251]: dtype np.str_
Out[251]: FalseIn [252]: np.str_ dtype
Out[252]: False 要检查 Series 是否包含分类数据请使用 hasattr(s, cat)
In [253]: hasattr(pd.Series([a], dtypecategory), cat)
Out[253]: TrueIn [254]: hasattr(pd.Series([a]), cat)
Out[254]: False 在类型为 category 的 Series 上使用 NumPy 函数应该不起作用因为 Categoricals 不是数值数据即使 .categories 是数值的情况下也是如此。
In [255]: s pd.Series(pd.Categorical([1, 2, 3, 4]))In [256]: try:.....: np.sum(s).....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Categorical with dtype category does not support reduction sum 注意
如果这样的函数有效请在 pandas-dev/pandas 提交 bug
apply 中的 dtype
pandas 目前不会在 apply 函数中保留 dtype如果你沿着行应用你会得到一个 object dtype 的 Series与获取一行相同 - 获取一个元素将返回一个基本类型并且沿着列应用也会转换为 object。NaN 值不受影响。你可以在应用函数之前使用 fillna 处理缺失值。
In [257]: df pd.DataFrame(.....: {.....: a: [1, 2, 3, 4],.....: b: [a, b, c, d],.....: cats: pd.Categorical([1, 2, 3, 2]),.....: }.....: ).....: In [258]: df.apply(lambda row: type(row[cats]), axis1)
Out[258]:
0 class int
1 class int
2 class int
3 class int
dtype: objectIn [259]: df.apply(lambda col: col.dtype, axis0)
Out[259]:
a int64
b object
cats category
dtype: object 分类索引
CategoricalIndex 是一种支持具有重复索引的索引的类型。这是围绕一个 Categorical 的容器允许有效地索引和存储具有大量重复元素的索引。有关更详细的解释请参阅高级索引文档。
设置索引将创建一个 CategoricalIndex
In [260]: cats pd.Categorical([1, 2, 3, 4], categories[4, 2, 3, 1])In [261]: strings [a, b, c, d]In [262]: values [4, 2, 3, 1]In [263]: df pd.DataFrame({strings: strings, values: values}, indexcats)In [264]: df.index
Out[264]: CategoricalIndex([1, 2, 3, 4], categories[4, 2, 3, 1], orderedFalse, dtypecategory)# This now sorts by the categories order
In [265]: df.sort_index()
Out[265]: strings values
4 d 1
2 b 2
3 c 3
1 a 4 副作用
从 Categorical 构建 Series 不会复制输入的 Categorical。这意味着对 Series 的更改在大多数情况下会改变原始的 Categorical
In [266]: cat pd.Categorical([1, 2, 3, 10], categories[1, 2, 3, 4, 10])In [267]: s pd.Series(cat, namecat)In [268]: cat
Out[268]:
[1, 2, 3, 10]
Categories (5, int64): [1, 2, 3, 4, 10]In [269]: s.iloc[0:2] 10In [270]: cat
Out[270]:
[10, 10, 3, 10]
Categories (5, int64): [1, 2, 3, 4, 10] 使用 copyTrue 来防止这种行为或者简单地不要重复使用 Categoricals
In [271]: cat pd.Categorical([1, 2, 3, 10], categories[1, 2, 3, 4, 10])In [272]: s pd.Series(cat, namecat, copyTrue)In [273]: cat
Out[273]:
[1, 2, 3, 10]
Categories (5, int64): [1, 2, 3, 4, 10]In [274]: s.iloc[0:2] 10In [275]: cat
Out[275]:
[1, 2, 3, 10]
Categories (5, int64): [1, 2, 3, 4, 10] 注意
在某些情况下当您提供一个 NumPy 数组而不是 Categorical 时也会发生这种情况使用整数数组例如 np.array([1,2,3,4])会表现出相同的行为而使用字符串数组例如 np.array([a,b,c,a])则不会。
内存使用
Categorical 的内存使用量与类别数量加上数据长度成正比。相比之下object dtype 是数据长度的常数倍。
In [242]: s pd.Series([foo, bar] * 1000)# object dtype
In [243]: s.nbytes
Out[243]: 16000# category dtype
In [244]: s.astype(category).nbytes
Out[244]: 2016 注意
如果类别数量接近数据长度Categorical 将使用几乎相同或更多的内存与等效的 object dtype 表示相比。
In [245]: s pd.Series([foo%04d % i for i in range(2000)])# object dtype
In [246]: s.nbytes
Out[246]: 16000# category dtype
In [247]: s.astype(category).nbytes
Out[247]: 20000 Categorical 不是一个 numpy 数组
当前分类数据和底层的 Categorical 是作为 Python 对象实现的而不是作为低级 NumPy 数组 dtype。这会导致一些问题。
NumPy 本身不知道新的 dtype
In [248]: try:.....: np.dtype(category).....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: data type category not understoodIn [249]: dtype pd.Categorical([a]).dtypeIn [250]: try:.....: np.dtype(dtype).....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Cannot interpret CategoricalDtype(categories[a], orderedFalse, categories_dtypeobject) as a data type Dtype 比较有效
In [251]: dtype np.str_
Out[251]: FalseIn [252]: np.str_ dtype
Out[252]: False 要检查 Series 是否包含分类数据请使用 hasattr(s, cat)
In [253]: hasattr(pd.Series([a], dtypecategory), cat)
Out[253]: TrueIn [254]: hasattr(pd.Series([a]), cat)
Out[254]: False 在类型为 category 的 Series 上使用 NumPy 函数应该不起作用因为 Categoricals 不是数值数据即使 .categories 是数值的情况下也是如此。
In [255]: s pd.Series(pd.Categorical([1, 2, 3, 4]))In [256]: try:.....: np.sum(s).....: except TypeError as e:.....: print(TypeError:, str(e)).....:
TypeError: Categorical with dtype category does not support reduction sum 注意
如果这样的函数有效请在 pandas-dev/pandas 提交 bug
apply 中的 dtype
pandas 目前不会在应用函数中保留 dtype如果沿着行应用你会得到一个dtype为object的Series与获取一行相同 - 获取一个元素将返回基本类型并且沿着列应用也会转换为 object。NaN值不受影响。你可以在应用函数之前使用fillna来处理缺失值。
In [257]: df pd.DataFrame(.....: {.....: a: [1, 2, 3, 4],.....: b: [a, b, c, d],.....: cats: pd.Categorical([1, 2, 3, 2]),.....: }.....: ).....: In [258]: df.apply(lambda row: type(row[cats]), axis1)
Out[258]:
0 class int
1 class int
2 class int
3 class int
dtype: objectIn [259]: df.apply(lambda col: col.dtype, axis0)
Out[259]:
a int64
b object
cats category
dtype: object 分类索引
CategoricalIndex是一种支持具有重复索引的索引的类型。这是围绕一个Categorical的容器允许高效地索引和存储具有大量重复元素的索引。查看高级索引文档以获取更详细的解释。
设置索引将创建一个CategoricalIndex
In [260]: cats pd.Categorical([1, 2, 3, 4], categories[4, 2, 3, 1])In [261]: strings [a, b, c, d]In [262]: values [4, 2, 3, 1]In [263]: df pd.DataFrame({strings: strings, values: values}, indexcats)In [264]: df.index
Out[264]: CategoricalIndex([1, 2, 3, 4], categories[4, 2, 3, 1], orderedFalse, dtypecategory)# This now sorts by the categories order
In [265]: df.sort_index()
Out[265]: strings values
4 d 1
2 b 2
3 c 3
1 a 4 副作用
从Categorical构建Series不会复制输入的Categorical。这意味着对Series的更改在大多数情况下会改变原始的Categorical
In [266]: cat pd.Categorical([1, 2, 3, 10], categories[1, 2, 3, 4, 10])In [267]: s pd.Series(cat, namecat)In [268]: cat
Out[268]:
[1, 2, 3, 10]
Categories (5, int64): [1, 2, 3, 4, 10]In [269]: s.iloc[0:2] 10In [270]: cat
Out[270]:
[10, 10, 3, 10]
Categories (5, int64): [1, 2, 3, 4, 10] 使用copyTrue来防止这种行为或者简单地不要重复使用Categoricals
In [271]: cat pd.Categorical([1, 2, 3, 10], categories[1, 2, 3, 4, 10])In [272]: s pd.Series(cat, namecat, copyTrue)In [273]: cat
Out[273]:
[1, 2, 3, 10]
Categories (5, int64): [1, 2, 3, 4, 10]In [274]: s.iloc[0:2] 10In [275]: cat
Out[275]:
[1, 2, 3, 10]
Categories (5, int64): [1, 2, 3, 4, 10] 注意
在某些情况下当您提供一个 NumPy 数组而不是Categorical时也会发生这种情况使用整数数组例如np.array([1,2,3,4])将表现出相同的行为而使用字符串数组例如np.array([a,b,c,a])则不会。