分类数据# 这是对 pandas 分类数据类型的介绍,包括与 R 的简短比较factor。 Categoricals是与统计中的分类变量相对应的 pandas 数据类型。分类变量具有有限且通常是固定数量的可能值(categories;levels在 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',我们使用了默认行为: 类别是从数据中推断出来的。 类别是无序的。 '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 # 分类类型的完整描述如下 categories:一系列唯一值且无缺失值 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()分类数据将产生与 aSeries或DataFrameof 类型类似的输出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.categories和s.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 类别也不得是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], 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