分类数据#

这是对 pandas 分类数据类型的介绍,包括与 R 的简短比较factor

Categoricals是与统计中的分类变量相对应的 pandas 数据类型。分类变量具有有限且通常是固定数量的可能值(categorieslevels在 R 中)。例如性别、社会阶层、血型、国家归属、观察时间或通过李克特量表进行评级。

与统计分类变量相反,分类数据可能有顺序(例如“强烈同意”与“同意”或“第一次观察”与“第二次观察”),但不可能进行数值运算(加法、除法等)。

分类数据的所有值都在categories或中np.nan。顺序是按 的顺序定义的categories,而不是值的词汇顺序。在内部,数据结构由一个数组和一个指向数组中实际值categories的整数数组组成。codescategories

分类数据类型在以下情况下很有用:

  • 仅包含几个不同值的字符串变量。将此类字符串变量转换为分类变量将节省一些内存,请参阅此处

  • 变量的词汇顺序与逻辑顺序(“一”、“二”、“三”)不同。通过转换为分类并指定类别的顺序,排序和最小/最大将使用逻辑顺序而不是词汇顺序,请参阅此处

  • 作为向其他 Python 库发出的信号,表明该列应被视为分类变量(例如,使用合适的统计方法或绘图类型)。

另请参阅有关分类的 API 文档

对象创建#

系列创作#

可以通过多种方式创建Seriesa 中的分类或列:DataFrame

dtype="category"通过在构建时指定Series

In [1]: s = pd.Series(["a", "b", "c", "a"], dtype="category")

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), right=False, labels=labels)

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对象传递给 aSeries或将其分配给 a DataFrame

In [10]: raw_cat = pd.Categorical(
   ....:     ["a", "b", "c", "a"], categories=["b", "c", "d"], ordered=False
   ....: )
   ....: 

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_cat

In [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

数据框创建#

与上一节中将单个列转换为分类类似,a 中的所有列都 DataFrame可以在构建期间或构建之后批量转换为分类。

dtype="category"这可以在构造过程中通过在构造函数中指定来完成DataFrame

In [17]: df = pd.DataFrame({"A": list("abca"), "B": list("bccd")}, dtype="category")

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可以使用以下命令进行批量转换DataFrame.astype()

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']

控制行为#

在上面我们传递的示例中dtype='category',我们使用了默认行为:

  1. 类别是从数据中推断出来的。

  2. 类别是无序的。

'category'要控制这些行为,请使用 的实例,而不是传递CategoricalDtype

In [26]: from pandas.api.types import CategoricalDtype

In [27]: s = pd.Series(["a", "b", "c", "a"])

In [28]: cat_type = CategoricalDtype(categories=["b", "c", "d"], ordered=True)

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']

同样,aCategoricalDtype可以与 a 一起使用,DataFrame以确保所有列中的类别一致。

In [31]: from pandas.api.types import CategoricalDtype

In [32]: df = pd.DataFrame({"A": list("abca"), "B": list("bccd")})

In [33]: cat_type = CategoricalDtype(categories=list("abcd"), ordered=True)

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可以通过编程方式确定参数 。categories = pd.unique(df.to_numpy().ravel())

如果您已经有了codesand 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)or 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: object

In [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: object

In [44]: np.asarray(s2)
Out[44]: array(['a', 'b', 'c', 'a'], dtype=object)

笔记

与 R 的函数相反factor,分类数据并不是将输入值转换为字符串;而是将输入值转换为字符串。类别最终的数据类型将与原始值相同。

笔记

与 R 的功能相反factor,目前无法在创建时分配/更改标签。用于categories在创建时间后更改类别。

分类Dtype #

分类类型的完整描述如下

  1. categories:一系列唯一值且无缺失值

  2. ordered:布尔值

该信息可以存储在CategoricalDtype.该categories参数是可选的,这意味着实际类别应该从创建时数据中存在的任何内容推断出来 pandas.Categorical。默认情况下,假定类别是无序的。

In [45]: from pandas.api.types import CategoricalDtype

In [46]: CategoricalDtype(["a", "b", "c"])
Out[46]: CategoricalDtype(categories=['a', 'b', 'c'], ordered=False, categories_dtype=object)

In [47]: CategoricalDtype(["a", "b", "c"], ordered=True)
Out[47]: CategoricalDtype(categories=['a', 'b', 'c'], ordered=True, categories_dtype=object)

In [48]: CategoricalDtype()
Out[48]: CategoricalDtype(categories=None, ordered=False, categories_dtype=None)

ACategoricalDtype可以用在 pandas 需要的任何地方dtype。例如,构造函数中的pandas.read_csv(), pandas.DataFrame.astype(), 或Series

笔记

为方便起见,当您希望类别的默认行为无序且等于数组中存在的设置值时,可以使用字符串'category'代替 a 。CategoricalDtype换句话说,dtype='category'相当于 dtype=CategoricalDtype()

平等语义#

只要两个实例CategoricalDtype具有相同的类别和顺序,它们就相等。比较两个无序分类时,categories不考虑的顺序。

In [49]: c1 = CategoricalDtype(["a", "b", "c"], ordered=False)

# Equal, since order is not considered when ordered=False
In [50]: c1 == CategoricalDtype(["b", "c", "a"], ordered=False)
Out[50]: True

# Unequal, since the second CategoricalDtype is ordered
In [51]: c1 == CategoricalDtype(["a", "b", "c"], ordered=True)
Out[51]: False

Compare的所有实例都CategoricalDtype等于 string 'category'

In [52]: c1 == "category"
Out[52]: True

描述

使用describe()分类数据将产生与 aSeriesDataFrameof 类型类似的输出string

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  2

In [56]: df["cat"].describe()
Out[56]: 
count     3
unique    2
top       c
freq      2
Name: cat, dtype: object

使用类别#

分类数据具有 acategories和 aordered属性,其中列出了它们的可能值以及排序是否重要。这些属性公开为s.cat.categoriess.cat.ordered。如果您不手动指定类别和排序,则会从传递的参数中推断出它们。

In [57]: s = pd.Series(["a", "b", "c", "a"], dtype="category")

In [58]: s.cat.categories
Out[58]: Index(['a', 'b', 'c'], dtype='object')

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'], dtype='object')

In [62]: s.cat.ordered
Out[62]: False

笔记

新的分类数据不会自动排序。您必须显式传递ordered=True以指示有序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'], dtype='object')

# 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"], dtype="category")

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

类别也不得是NaNValueError提出:

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], dtype='object')

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", "-"], dtype="category")

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"], ordered=False))

In [89]: s = s.sort_values()

In [90]: s = pd.Series(["a", "b", "c", "a"]).astype(CategoricalDtype(ordered=True))

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], dtype="category")

In [97]: s = s.cat.set_categories([2, 3, 1], ordered=True)

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], dtype="category")

In [103]: s = s.cat.reorder_categories([2, 3, 1], ordered=True)

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"],
   .....:             ordered=True,
   .....:         ),
   .....:         "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

比较#

在三种情况下可以将分类数据与其他对象进行比较:

  • 将等式 (==!=) 与与分类数据长度相同的类似列表的对象(列表、系列、数组……)进行比较。

  • 当和相同时,分类数据与另一个分类系列的所有比较(==!=>>=<和)。<=ordered==Truecategories

  • 分类数据与标量的所有比较。

所有其他比较,尤其是两个具有不同类别的分类或一个分类与任何类似列表的对象的“不相等”比较,都会引发TypeError.

笔记

Series分类数据与 a 、np.array或具有不同类别或排序的分类数据的任何“非相等”比较list都会产生 a,TypeError因为自定义类别排序可以用两种方式解释:一种考虑排序,另一种不考虑排序。

In [112]: cat = pd.Series([1, 2, 3]).astype(CategoricalDtype([3, 2, 1], ordered=True))

In [113]: cat_base = pd.Series([2, 2, 2]).astype(CategoricalDtype([3, 2, 1], ordered=True))

In [114]: cat_base2 = pd.Series([2, 2, 2]).astype(CategoricalDtype(ordered=True))

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: bool

In [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: bool

In [121]: cat == np.array([1, 2, 3])
Out[121]: 
0    True
1    True
2    True
dtype: bool

In [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"], ordered=False)

In [128]: c2 = pd.Categorical(["a", "b"], categories=["b", "a"], ordered=False)

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()也会在 时显示“未使用”类别observed=False

In [132]: columns = pd.Categorical(
   .....:     ["One", "One", "Two"], categories=["One", "Two", "Three"], ordered=True
   .....: )
   .....: 

In [133]: df = pd.DataFrame(
   .....:     data=[[1, 2, 3], [4, 5, 6]],
   .....:     columns=pd.MultiIndex.from_arrays([["A", "B", "B"], columns]),
   .....: ).T
   .....: 

In [134]: df.groupby(level=1, observed=False).sum()
Out[134]: 
       0  1
One    3  9
Two    3  6
Three  0  0

在以下情况下,Groupby 还将显示“未使用”类别observed=False

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", observed=False).mean()
Out[137]: 
      values
cats        
a        1.0
b        2.0
c        4.0
d        NaN

In [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"], observed=False).mean()
Out[140]: 
        values
cats B        
a    c     1.0
     d     2.0
b    c     3.0
     d     4.0
c    c     NaN
     d     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, values="values", index=["A", "B"], observed=False)
Out[143]: 
     values
A B        
a c     1.0
  d     2.0
b c     3.0
  d     4.0

数据整理#

优化后的 pandas 数据访问方法.loc.iloc.at.iat可以正常工作。唯一的区别是返回类型(用于获取)并且只能categories分配已经存在的值。

获取#

如果切片操作返回DataFrame类型为 的 a 或列 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"], dtype="category", index=idx)

In [146]: values = [1, 2, 2, 2, 3, 4, 5]

In [147]: df = pd.DataFrame({"cats": cats, "values": values}, index=idx)

In [148]: df.iloc[2:4, :]
Out[148]: 
  cats  values
j    b       2
k    b       2

In [149]: df.iloc[2:4, :].dtypes
Out[149]: 
cats      category
values       int64
dtype: object

In [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]: 'a'

In [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,R的函数factor(c(1,2,3))[1] 返回单个值factor

Series要获取type 的单个值category,请传入一个包含单个值的列表:

In [156]: df.loc[["h"], "cats"]
Out[156]: 
h    x
Name: cats, dtype: category
Categories (3, object): ['x', 'y', 'z']

字符串和日期时间访问器#

如果类型合适,访问器.dt.str将起作用:s.cat.categories

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: bool

In [161]: date_s = pd.Series(pd.date_range("1/1/2015", periods=5))

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) 的类型与您 在该类型的a 上使用.str.<method>/的类型相同(而不是类型!)。.dt.<method>Seriescategory

这意味着,a 的访问器上的方法和属性的返回值 与转换为其中一种类型的Seriesthis 的访问器上的方法和属性的返回值 将相等:Seriescategory

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]: True

In [168]: ret_s == ret_cat
Out[168]: 
0    True
1    True
2    True
3    True
dtype: bool

笔记

工作已完成categories,然后Series建造新的。如果您有一个Seriesof 类型的字符串,其中有很多元素重复(即 中唯一元素的数量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}, index=idx)

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       1

In [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       1

In [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

将 a 分配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  a

In [183]: df.dtypes
Out[183]: 
a    object
b    object
dtype: object

合并/串联#

默认情况下,组合SeriesDataFrames包含相同类别会产生categorydtype,否则结果将取决于基础类别的 dtype。导致非分类数据类型的合并可能会具有更高的内存使用量。使用.astypeunion_categoricals确保category结果。

In [184]: from pandas.api.types import union_categoricals

# same categories
In [185]: s1 = pd.Series(["a", "b"], dtype="category")

In [186]: s2 = pd.Series(["a", "b", "a"], dtype="category")

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"], dtype="category")

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], dtype="category")

In [191]: float_cats = pd.Series([3.0, 4.0], dtype="category")

In [192]: pd.concat([int_cats, float_cats])
Out[192]: 
0    1.0
1    2.0
0    3.0
1    4.0
dtype: float64

In [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

精氨酸1

精氨酸2

完全相同的

结果

类别

类别

真的

类别

类别(对象)

类别(对象)

错误的

对象(推断出 dtype)

类别(整数