python - Pandas - 根据具有重复名称的重复键值对列重塑数据框列
问题描述
我从一个 API 接收一个数据框,该数据框在初始列中包含某些实体的数据,而后面的列包含这些实体的元数据。数据是专有的,所以我无法准确显示,但数据框的结构如下所示:
idx name types value value_date desc group owner key_name key_value key_name key_value key_name key_value
1 name1 type1 45 2021-05-30 name1-type1 G1 O1 k1 A1 k2 A2 k3 A3
2 name1 type2 23 2021-05-30 name1-type2 G1 O1 k3 B3 NaN NaN k2 B2
3 name2 type1 41 2021-05-30 name2-type1 G2 O2 NaN NaN k1 C1 NaN NaN
4 name3 type1 39 2021-05-30 name3-type1 G1 O1 k1 D1 k3 D3 NaN NaN
5 name4 type1 40 2021-05-30 name4-type1 G3 O3 k1 E1 k3 E3 k2 E2
6 name4 type2 21 2021-05-30 name4-type2 G3 O3 k3 F3 k2 F2 k1 F1
7 name4 type3 11 2021-05-30 name4-type3 G3 O3 NaN NaN NaN NaN NaN NaN
8 name5 type1 44 2021-05-30 name5-type1 G1 O1 NaN NaN k1 H1 NaN NaN
9 name6 type1 49 2021-05-30 name6-type1 G2 O2 NaN NaN k2 I2 NaN NaN
10 name6 type2 26 2021-05-30 name6-type2 G2 O2 k1 J1 NaN NaN k3 J3
下面的代码将生成上述示例数据框:
df = pd.DataFrame( {'idx': {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10}, 'name': {0: 'name1', 1: 'name1', 2: 'name2', 3: 'name3', 4: 'name4', 5: 'name4', 6: 'name4', 7: 'name5', 8: 'name6', 9: 'name6'}, 'types': {0: 'type1', 1: 'type2', 2: 'type1', 3: 'type1', 4: 'type1', 5: 'type2', 6: 'type3', 7: 'type1', 8: 'type1', 9: 'type2'}, 'value': {0: 45, 1: 23, 2: 41, 3: 39, 4: 40, 5: 21, 6: 11, 7: 44, 8: 49, 9: 26}, 'value_date': {0: '2021-05-30', 1: '2021-05-30', 2: '2021-05-30', 3: '2021-05-30', 4: '2021-05-30', 5: '2021-05-30', 6: '2021-05-30', 7: '2021-05-30', 8: '2021-05-30', 9: '2021-05-30'}, 'desc': {0: 'name1-type1', 1: 'name1-type2', 2: 'name2-type1', 3: 'name3-type1', 4: 'name4-type1', 5: 'name4-type2', 6: 'name4-type3', 7: 'name5-type1', 8: 'name6-type1', 9: 'name6-type2'}, 'group': {0: 'G1', 1: 'G1', 2: 'G2', 3: 'G1', 4: 'G3', 5: 'G3', 6: 'G3', 7: 'G1', 8: 'G2', 9: 'G2'}, 'owner': {0: 'O1', 1: 'O1', 2: 'O2', 3: 'O1', 4: 'O3', 5: 'O3', 6: 'O3', 7: 'O1', 8: 'O2', 9: 'O2'}, 'key_name': {0: 'k1', 1: 'k3', 2: float('nan'), 3: 'k1', 4: 'k1', 5: 'k3', 6: float('nan'), 7: float('nan'), 8: float('nan'), 9: 'k1'}, 'key_value': {0: 'A1', 1: 'B3', 2: float('nan'), 3: 'D1', 4: 'E1', 5: 'F3', 6: float('nan'), 7: float('nan'), 8: float('nan'), 9: 'J1'}, 'key_name_1': {0: 'k2', 1: float('nan'), 2: 'k1', 3: 'k3', 4: 'k3', 5: 'k2', 6: float('nan'), 7: 'k1', 8: 'k2', 9: float('nan')}, 'key_value_1': {0: 'A2', 1: float('nan'), 2: 'C1', 3: 'D3', 4: 'E3', 5: 'F2', 6: float('nan'), 7: 'H1', 8: 'I2', 9: float('nan')}, 'key_name_2': {0: 'k3', 1: 'k2', 2: float('nan'), 3: float('nan'), 4: 'k2', 5: 'k1', 6: float('nan'), 7: float('nan'), 8: float('nan'), 9: 'k3'}, 'key_value_2': {0: 'A3', 1: 'B2', 2: float('nan'), 3: float('nan'), 4: 'E2', 5: 'F1', 6: float('nan'), 7: float('nan'), 8: float('nan'), 9: 'J3'}} )
df.rename(columns={'key_name_1':'key_name','key_value_1':'key_value','key_name_2':'key_name','key_value_2':'key_value'}, inplace=True)
注意owner之后重复 的"key_name", "key_value" 对列。这些列包含元数据值,元数据名称和值也是如此。现在一个实体可以有不同数量的元数据,所以对于某些行,可能有 15-20 个这样的对列。key_name
key_value
NaN
如果该元数据不适用于该实体,则其他行将具有。
我想将此数据框重塑为以下数据框,其中每个都key_name
成为列,并且该列中的值应该是key_value
每个实体的值(如果适用):
idx name types value value_date desc group owner k1 k2 k3
1 name1 type1 45 2021-05-30 name1-type1 G1 O1 A1 A2 A3
2 name1 type2 23 2021-05-30 name1-type2 G1 O1 NaN B2 B3
3 name2 type1 41 2021-05-30 name2-type1 G2 O2 C1 NaN NaN
4 name3 type1 39 2021-05-30 name3-type1 G1 O1 D1 NaN D3
5 name4 type1 40 2021-05-30 name4-type1 G3 O3 E1 E2 E3
6 name4 type2 21 2021-05-30 name4-type2 G3 O3 F1 F2 F3
7 name4 type3 11 2021-05-30 name4-type3 G3 O3 NaN NaN NaN
8 name5 type1 44 2021-05-30 name5-type1 G1 O1 H1 NaN NaN
9 name6 type1 49 2021-05-30 name6-type1 G2 O2 NaN I2 NaN
10 name6 type2 26 2021-05-30 name6-type2 G2 O2 J1 NaN J3
现在的问题是,对于问题数据框中的每个实体,这些“key_name”、“key_value”对数据的顺序不同。如果您看到name1-type1,按顺序首先出现,然后, .... 但对于name4-type2, 首先出现,然后是,完全相反。(
k1
, A1)
(
k2
, A2)
(
k3
, F3)
k2
k1
我怀疑 API 中发生的情况是,对于每个唯一实体,数据都在循环中获取,然后在稍后连接,因此连接有效,但元数据的顺序并不能保证。我无法修复 API,因为它不是我的,所以我需要修复它的输出才能继续。
解决方案
来自pyjanitor的辅助工具的组合可能会有所帮助:
# pip install pyjanitor
import janitor
import pandas as pd
(df.pivot_longer(index = slice("idx", "owner"),
names_to=("names", "values"),
names_pattern=["name", "value"],
values_to="valued")
.dropna()
.pivot_wider(index = slice("idx", "owner"),
names_from="names")
.merge(df.loc[:, "idx":"owner"],
how = 'outer')
.sort_values('idx')
)
idx name types value value_date desc group owner k1 k3 k2
0 1 name1 type1 45 2021-05-30 name1-type1 G1 O1 A1 A3 A2
1 2 name1 type2 23 2021-05-30 name1-type2 G1 O1 NaN B3 B2
2 3 name2 type1 41 2021-05-30 name2-type1 G2 O2 C1 NaN NaN
3 4 name3 type1 39 2021-05-30 name3-type1 G1 O1 D1 D3 NaN
4 5 name4 type1 40 2021-05-30 name4-type1 G3 O3 E1 E3 E2
5 6 name4 type2 21 2021-05-30 name4-type2 G3 O3 F1 F3 F2
9 7 name4 type3 11 2021-05-30 name4-type3 G3 O3 NaN NaN NaN
6 8 name5 type1 44 2021-05-30 name5-type1 G1 O1 H1 NaN NaN
7 9 name6 type1 49 2021-05-30 name6-type1 G2 O2 NaN NaN I2
8 10 name6 type2 26 2021-05-30 name6-type2 G2 O2 J1 J3 NaN
pivot_longer只是提供了一种从宽到长重塑的更简单方法 - 对于您的情况,有一个模式(一些列有name
,一些.value
) - 我们使用该模式names_pattern
来翻转表格。pivot_wider是建立pandas pivot
在这种情况下并且只是语法糖 - 这里使用的唯一原因只是因为我不想输入所有索引名称:),但你可以跳过它并使用pivot
. 结果不包括idx
7,因为它完全为空……与原来的合并df
重新引入它。
他们只是帮手,所以我们有能力不包括他们。这是实现此目的的一种可能方法:
熔化列,保留原始索引:
index = [*df.columns[:8]]
base = (df.melt(index, value_name = "valued", ignore_index = False)
.dropna()
.drop(columns="variable"))
提取valued
仅以 开头的数据框K
,并在其中附加index
到现有索引:
Ks = (base.loc[base.valued.str.startswith("k")]
.set_index([*index], append = True)
)
提取valued
不以以下开头的数据框K
:
non_Ks = (base.loc[~base.valued.str.startswith("k")]
.rename(columns={"valued":"flip_this_column"})
.set_index(index, append = True)
)
结合Ks
,non_Ks
删除不相关的标签并重置索引:
(pd.concat([Ks, non_Ks], axis = 'columns')
.set_index('valued',append=True)
.unstack()
.droplevel(0, axis = 'columns')
.rename_axis(columns=None)
.reset_index(index)
)
idx name types value value_date desc group owner k1 k2 k3
0 1 name1 type1 45 2021-05-30 name1-type1 G1 O1 A1 A2 A3
1 2 name1 type2 23 2021-05-30 name1-type2 G1 O1 NaN B2 B3
2 3 name2 type1 41 2021-05-30 name2-type1 G2 O2 C1 NaN NaN
3 4 name3 type1 39 2021-05-30 name3-type1 G1 O1 D1 NaN D3
4 5 name4 type1 40 2021-05-30 name4-type1 G3 O3 E1 E2 E3
5 6 name4 type2 21 2021-05-30 name4-type2 G3 O3 F1 F2 F3
7 8 name5 type1 44 2021-05-30 name5-type1 G1 O1 H1 NaN NaN
8 9 name6 type1 49 2021-05-30 name6-type1 G2 O2 NaN I2 NaN
9 10 name6 type2 26 2021-05-30 name6-type2 G2 O2 J1 NaN J3
推荐阅读
- delphi - TImageList/TListview:正确的设计选择?
- php - 使用 codeigniter _remap(); 带有波斯字符
- c - 当使用 realloc 减少数组时,内存会发生什么?
- java - 使用静态变量打破递归
- sql - Oracle 中的动态透视
- python - 使用 SimpleHTTPServer 发送缓冲区而不是文件
- image - dart - 复制图像并保存在同一目录中
- c++ - 成员初始化器列表的使用如何防止在 C++ 中创建冗余对象?
- php - laravel 5.7 如何将一个控制器的变量 id 传递给另一个控制器
- postgresql - 如何将相关组保持在一起?