Pandas 2.2 中文官方教程和指南(十四)(3)https://developer.aliyun.com/article/1509845
melt()
和 wide_to_long()
顶级melt()
函数及其对应的DataFrame.melt()
对于将DataFrame
整理成一个格式很有用,其中一个或多个列是标识变量,而所有其他列,被认为是测量变量,都被“展开”到行轴上,仅留下两个非标识列,“变量”和“值”。这些列的名称可以通过提供 var_name
和 value_name
参数进行自定义。
In [47]: cheese = pd.DataFrame( ....: { ....: "first": ["John", "Mary"], ....: "last": ["Doe", "Bo"], ....: "height": [5.5, 6.0], ....: "weight": [130, 150], ....: } ....: ) ....: In [48]: cheese Out[48]: first last height weight 0 John Doe 5.5 130 1 Mary Bo 6.0 150 In [49]: cheese.melt(id_vars=["first", "last"]) Out[49]: first last variable value 0 John Doe height 5.5 1 Mary Bo height 6.0 2 John Doe weight 130.0 3 Mary Bo weight 150.0 In [50]: cheese.melt(id_vars=["first", "last"], var_name="quantity") Out[50]: first last quantity value 0 John Doe height 5.5 1 Mary Bo height 6.0 2 John Doe weight 130.0 3 Mary Bo weight 150.0
使用melt()
转换 DataFrame 时,索引会被忽略。通过将 ignore_index=False
参数设置为 False
(默认为 True
),可以保留原始索引值。ignore_index=False
会导致索引值重复。
In [51]: index = pd.MultiIndex.from_tuples([("person", "A"), ("person", "B")]) In [52]: cheese = pd.DataFrame( ....: { ....: "first": ["John", "Mary"], ....: "last": ["Doe", "Bo"], ....: "height": [5.5, 6.0], ....: "weight": [130, 150], ....: }, ....: index=index, ....: ) ....: In [53]: cheese Out[53]: first last height weight person A John Doe 5.5 130 B Mary Bo 6.0 150 In [54]: cheese.melt(id_vars=["first", "last"]) Out[54]: first last variable value 0 John Doe height 5.5 1 Mary Bo height 6.0 2 John Doe weight 130.0 3 Mary Bo weight 150.0 In [55]: cheese.melt(id_vars=["first", "last"], ignore_index=False) Out[55]: first last variable value person A John Doe height 5.5 B Mary Bo height 6.0 A John Doe weight 130.0 B Mary Bo weight 150.0
wide_to_long()
类似于 melt()
,但具有更多的列匹配自定义选项。
In [56]: dft = pd.DataFrame( ....: { ....: "A1970": {0: "a", 1: "b", 2: "c"}, ....: "A1980": {0: "d", 1: "e", 2: "f"}, ....: "B1970": {0: 2.5, 1: 1.2, 2: 0.7}, ....: "B1980": {0: 3.2, 1: 1.3, 2: 0.1}, ....: "X": dict(zip(range(3), np.random.randn(3))), ....: } ....: ) ....: In [57]: dft["id"] = dft.index In [58]: dft Out[58]: A1970 A1980 B1970 B1980 X id 0 a d 2.5 3.2 1.519970 0 1 b e 1.2 1.3 -0.493662 1 2 c f 0.7 0.1 0.600178 2 In [59]: pd.wide_to_long(dft, ["A", "B"], i="id", j="year") Out[59]: X A B id year 0 1970 1.519970 a 2.5 1 1970 -0.493662 b 1.2 2 1970 0.600178 c 0.7 0 1980 1.519970 d 3.2 1 1980 -0.493662 e 1.3 2 1980 0.600178 f 0.1
get_dummies()
和 from_dummies()
将Series
的分类变量转换为“虚拟”或“指示符”时,get_dummies()
会创建一个新的DataFrame
,其中包含唯一变量的列,值表示每行中这些变量的存在情况。
In [60]: df = pd.DataFrame({"key": list("bbacab"), "data1": range(6)}) In [61]: pd.get_dummies(df["key"]) Out[61]: a b c 0 False True False 1 False True False 2 True False False 3 False False True 4 True False False 5 False True False In [62]: df["key"].str.get_dummies() Out[62]: a b c 0 0 1 0 1 0 1 0 2 1 0 0 3 0 0 1 4 1 0 0 5 0 1 0
prefix
为列名添加一个前缀,这对于将结果与原始DataFrame
合并很有用:
In [63]: dummies = pd.get_dummies(df["key"], prefix="key") In [64]: dummies Out[64]: key_a key_b key_c 0 False True False 1 False True False 2 True False False 3 False False True 4 True False False 5 False True False In [65]: df[["data1"]].join(dummies) Out[65]: data1 key_a key_b key_c 0 0 False True False 1 1 False True False 2 2 True False False 3 3 False False True 4 4 True False False 5 5 False True False
这个函数经常与诸如 cut()
这样的离散化函数一起使用:
In [66]: values = np.random.randn(10) In [67]: values Out[67]: array([ 0.2742, 0.1329, -0.0237, 2.4102, 1.4505, 0.2061, -0.2519, -2.2136, 1.0633, 1.2661]) In [68]: bins = [0, 0.2, 0.4, 0.6, 0.8, 1] In [69]: pd.get_dummies(pd.cut(values, bins)) Out[69]: (0.0, 0.2] (0.2, 0.4] (0.4, 0.6] (0.6, 0.8] (0.8, 1.0] 0 False True False False False 1 True False False False False 2 False False False False False 3 False False False False False 4 False False False False False 5 False True False False False 6 False False False False False 7 False False False False False 8 False False False False False 9 False False False False False
get_dummies()
也接受一个DataFrame
。默认情况下,object
、string
或categorical
类型的列会被编码为虚拟变量,其他列不变。
In [70]: df = pd.DataFrame({"A": ["a", "b", "a"], "B": ["c", "c", "b"], "C": [1, 2, 3]}) In [71]: pd.get_dummies(df) Out[71]: C A_a A_b B_b B_c 0 1 True False False True 1 2 False True False True 2 3 True False True False
指定 columns
关键字会编码任何类型的列。
In [72]: pd.get_dummies(df, columns=["A"]) Out[72]: B C A_a A_b 0 c 1 True False 1 c 2 False True 2 b 3 True False
与Series
版本一样,可以为 prefix
和 prefix_sep
传递值。默认情况下,列名用作前缀,_
用作前缀分隔符。可以以 3 种方式指定 prefix
和 prefix_sep
:
- 字符串:对于要编码的每列,使用相同的值作为
prefix
或prefix_sep
。 - 列表:必须与被编码的列数相同。
- 字典:将列名映射到前缀。
In [73]: simple = pd.get_dummies(df, prefix="new_prefix") In [74]: simple Out[74]: C new_prefix_a new_prefix_b new_prefix_b new_prefix_c 0 1 True False False True 1 2 False True False True 2 3 True False True False In [75]: from_list = pd.get_dummies(df, prefix=["from_A", "from_B"]) In [76]: from_list Out[76]: C from_A_a from_A_b from_B_b from_B_c 0 1 True False False True 1 2 False True False True 2 3 True False True False In [77]: from_dict = pd.get_dummies(df, prefix={"B": "from_B", "A": "from_A"}) In [78]: from_dict Out[78]: C from_A_a from_A_b from_B_b from_B_c 0 1 True False False True 1 2 False True False True 2 3 True False True False
在将结果提供给统计模型时,为避免共线性,请指定 drop_first=True
。
In [79]: s = pd.Series(list("abcaa")) In [80]: pd.get_dummies(s) Out[80]: a b c 0 True False False 1 False True False 2 False False True 3 True False False 4 True False False In [81]: pd.get_dummies(s, drop_first=True) Out[81]: b c 0 False False 1 True False 2 False True 3 False False 4 False False
当列只包含一个级别时,结果中将省略该列。
In [82]: df = pd.DataFrame({"A": list("aaaaa"), "B": list("ababc")}) In [83]: pd.get_dummies(df) Out[83]: A_a B_a B_b B_c 0 True True False False 1 True False True False 2 True True False False 3 True False True False 4 True False False True In [84]: pd.get_dummies(df, drop_first=True) Out[84]: B_b B_c 0 False False 1 True False 2 False False 3 True False 4 False True
可以使用 dtype
参数将值转换为不同类型。
In [85]: df = pd.DataFrame({"A": list("abc"), "B": [1.1, 2.2, 3.3]}) In [86]: pd.get_dummies(df, dtype=np.float32).dtypes Out[86]: B float64 A_a float32 A_b float32 A_c float32 dtype: object
版本 1.5.0 中的新功能。
from_dummies()
将 get_dummies()
的输出转换回指示值的分类值 Series
。
In [87]: df = pd.DataFrame({"prefix_a": [0, 1, 0], "prefix_b": [1, 0, 1]}) In [88]: df Out[88]: prefix_a prefix_b 0 0 1 1 1 0 2 0 1 In [89]: pd.from_dummies(df, sep="_") Out[89]: prefix 0 b 1 a 2 b
虚拟编码数据只需要包含 k - 1
个类别,此时最后一个类别是默认类别。默认类别可以使用 default_category
修改。
In [90]: df = pd.DataFrame({"prefix_a": [0, 1, 0]}) In [91]: df Out[91]: prefix_a 0 0 1 1 2 0 In [92]: pd.from_dummies(df, sep="_", default_category="b") Out[92]: prefix 0 b 1 a 2 b
explode()
对于具有嵌套、类似列表的值的 DataFrame
列,explode()
将每个类似列表的值转换为单独的行。生成的 Index
将根据原始行的索引标签重复:
In [93]: keys = ["panda1", "panda2", "panda3"] In [94]: values = [["eats", "shoots"], ["shoots", "leaves"], ["eats", "leaves"]] In [95]: df = pd.DataFrame({"keys": keys, "values": values}) In [96]: df Out[96]: keys values 0 panda1 [eats, shoots] 1 panda2 [shoots, leaves] 2 panda3 [eats, leaves] In [97]: df["values"].explode() Out[97]: 0 eats 0 shoots 1 shoots 1 leaves 2 eats 2 leaves Name: values, dtype: object
DataFrame.explode
也可以将 DataFrame
中的列扩展。
In [98]: df.explode("values") Out[98]: keys values 0 panda1 eats 0 panda1 shoots 1 panda2 shoots 1 panda2 leaves 2 panda3 eats 2 panda3 leaves
Series.explode()
将空列表替换为缺失值指示符,并保留标量条目。
In [99]: s = pd.Series([[1, 2, 3], "foo", [], ["a", "b"]]) In [100]: s Out[100]: 0 [1, 2, 3] 1 foo 2 [] 3 [a, b] dtype: object In [101]: s.explode() Out[101]: 0 1 0 2 0 3 1 foo 2 NaN 3 a 3 b dtype: object
逗号分隔的字符串值可以拆分为列表中的单个值,然后扩展到新行。
In [102]: df = pd.DataFrame([{"var1": "a,b,c", "var2": 1}, {"var1": "d,e,f", "var2": 2}]) In [103]: df.assign(var1=df.var1.str.split(",")).explode("var1") Out[103]: var1 var2 0 a 1 0 b 1 0 c 1 1 d 2 1 e 2 1 f 2
crosstab()
使用 crosstab()
计算两个(或更多)因子的交叉制表。默认情况下,crosstab()
计算因子的频率表,除非传递了值数组和聚合函数。
任何传递的 Series
将使用其名称属性,除非为交叉制表指定了行或列名称
In [104]: a = np.array(["foo", "foo", "bar", "bar", "foo", "foo"], dtype=object) In [105]: b = np.array(["one", "one", "two", "one", "two", "one"], dtype=object) In [106]: c = np.array(["dull", "dull", "shiny", "dull", "dull", "shiny"], dtype=object) In [107]: pd.crosstab(a, [b, c], rownames=["a"], colnames=["b", "c"]) Out[107]: b one two c dull shiny dull shiny a bar 1 0 0 1 foo 2 1 1 0
如果 crosstab()
只接收两个 Series
,它将提供一个频率表。
In [108]: df = pd.DataFrame( .....: {"A": [1, 2, 2, 2, 2], "B": [3, 3, 4, 4, 4], "C": [1, 1, np.nan, 1, 1]} .....: ) .....: In [109]: df Out[109]: A B C 0 1 3 1.0 1 2 3 1.0 2 2 4 NaN 3 2 4 1.0 4 2 4 1.0 In [110]: pd.crosstab(df["A"], df["B"]) Out[110]: B 3 4 A 1 1 0 2 1 3
crosstab()
还可以总结为Categorical
数据。
In [111]: foo = pd.Categorical(["a", "b"], categories=["a", "b", "c"]) In [112]: bar = pd.Categorical(["d", "e"], categories=["d", "e", "f"]) In [113]: pd.crosstab(foo, bar) Out[113]: col_0 d e row_0 a 1 0 b 0 1
对于Categorical
数据,要包含所有数据类别,即使实际数据不包含特定类别的任何实例,使用dropna=False
。
In [114]: pd.crosstab(foo, bar, dropna=False) Out[114]: col_0 d e f row_0 a 1 0 0 b 0 1 0 c 0 0 0
标准化
频率表也可以通过normalize
参数显示百分比而不是计数:
In [115]: pd.crosstab(df["A"], df["B"], normalize=True) Out[115]: B 3 4 A 1 0.2 0.0 2 0.2 0.6
normalize
还可以在每行或每列内规范化值:
In [116]: pd.crosstab(df["A"], df["B"], normalize="columns") Out[116]: B 3 4 A 1 0.5 0.0 2 0.5 1.0
crosstab()
还可以接受第三个Series
和一个聚合函数(aggfunc
),该函数将应用于第三个Series
的值,这些值在由前两个Series
定义的每个组内:
In [117]: pd.crosstab(df["A"], df["B"], values=df["C"], aggfunc="sum") Out[117]: B 3 4 A 1 1.0 NaN 2 1.0 2.0
添加边际
margins=True
将添加一个带有All
标签的行和列,该标签在行和列上的类别中部分组聚合:
In [118]: pd.crosstab( .....: df["A"], df["B"], values=df["C"], aggfunc="sum", normalize=True, margins=True .....: ) .....: Out[118]: B 3 4 All A 1 0.25 0.0 0.25 2 0.25 0.5 0.75 All 0.50 0.5 1.00
标准化
频率表也可以通过normalize
参数显示百分比而不是计数:
In [115]: pd.crosstab(df["A"], df["B"], normalize=True) Out[115]: B 3 4 A 1 0.2 0.0 2 0.2 0.6
normalize
还可以在每行或每列内规范化值:
In [116]: pd.crosstab(df["A"], df["B"], normalize="columns") Out[116]: B 3 4 A 1 0.5 0.0 2 0.5 1.0
crosstab()
还可以接受第三个Series
和一个聚合函数(aggfunc
),该函数将应用于第三个Series
的值,这些值在由前两个Series
定义的每个组内:
In [117]: pd.crosstab(df["A"], df["B"], values=df["C"], aggfunc="sum") Out[117]: B 3 4 A 1 1.0 NaN 2 1.0 2.0
添加边际
margins=True
将添加一个带有All
标签的行和列,该标签在行和列上的类别中部分组聚合:
In [118]: pd.crosstab( .....: df["A"], df["B"], values=df["C"], aggfunc="sum", normalize=True, margins=True .....: ) .....: Out[118]: B 3 4 All A 1 0.25 0.0 0.25 2 0.25 0.5 0.75 All 0.50 0.5 1.00
cut()
cut()
函数计算输入数组的值的分组,并经常用于将连续变量转换为离散或分类变量:
整数bins
将形成等宽箱。
In [119]: ages = np.array([10, 15, 13, 12, 23, 25, 28, 59, 60]) In [120]: pd.cut(ages, bins=3) Out[120]: [(9.95, 26.667], (9.95, 26.667], (9.95, 26.667], (9.95, 26.667], (9.95, 26.667], (9.95, 26.667], (26.667, 43.333], (43.333, 60.0], (43.333, 60.0]] Categories (3, interval[float64, right]): [(9.95, 26.667] < (26.667, 43.333] < (43.333, 60.0]]
一个有序的箱边列表将为每个变量分配一个区间。
In [121]: pd.cut(ages, bins=[0, 18, 35, 70]) Out[121]: [(0, 18], (0, 18], (0, 18], (0, 18], (18, 35], (18, 35], (18, 35], (35, 70], (35, 70]] Categories (3, interval[int64, right]): [(0, 18] < (18, 35] < (35, 70]]
如果bins
关键字是一个IntervalIndex
,那么这些将用于对传递的数据进行分箱。
In [122]: pd.cut(ages, bins=pd.IntervalIndex.from_breaks([0, 40, 70])) Out[122]: [(0, 40], (0, 40], (0, 40], (0, 40], (0, 40], (0, 40], (0, 40], (40, 70], (40, 70]] Categories (2, interval[int64, right]): [(0, 40] < (40, 70]]
factorize()
factorize()
将一维数值编码为整数标签。缺失值被编码为-1
。
In [123]: x = pd.Series(["A", "A", np.nan, "B", 3.14, np.inf]) In [124]: x Out[124]: 0 A 1 A 2 NaN 3 B 4 3.14 5 inf dtype: object In [125]: labels, uniques = pd.factorize(x) In [126]: labels Out[126]: array([ 0, 0, -1, 1, 2, 3]) In [127]: uniques Out[127]: Index(['A', 'B', 3.14, inf], dtype='object')
Categorical
会类似地对一维值进行编码,以进行更多的分类操作。
In [128]: pd.Categorical(x) Out[128]: ['A', 'A', NaN, 'B', 3.14, inf] Categories (4, object): [3.14, inf, 'A', 'B']