python - Pandas:扁平化树结构
问题描述
我有一个由以下表示的类别树。
import pandas as pd
asset_tree = [
{'id': 1, 'name': 'Linear Asset', 'parent_id': -1},
{'id': 2, 'name': 'Lateral', 'parent_id': 1},
{'id': 3, 'name': 'Main', 'parent_id': 1},
{'id': 4, 'name': 'Point Asset', 'parent_id': -1},
{'id': 5, 'name': 'Fountain', 'parent_id': 4},
{'id': 6, 'name': 'Hydrant', 'parent_id': 4}
]
tree = pd.DataFrame(asset_tree)
print(tree)
这给了我一个如下的数据框:
id name parent_id
0 1 Linear Asset -1
1 2 Lateral 1
2 3 Main 1
3 4 Point Asset -1
4 5 Fountain 4
5 6 Hydrant 4
树中最高节点的 parent_id 等于 -1,因此树可以用图形表示如下:
Linear Asset
| - Lateral
| - Main
Point Asset
| - Fountain
| - Hydrant
我需要生成以下数据框。
id name parent_id flat_name
0 1 Linear Asset -1 Linear Asset
1 2 Lateral 1 Linear Asset : Lateral
2 3 Main 1 Linear Asset : Main
3 4 Point Asset -1 Point Asset
4 5 Fountain 4 Point Asset : Fountain
5 6 Hydrant 4 Point Asset : Hydrant
树是动态生成的,可以有任意数量的级别,所以下面的树
asset_tree = [
{'id': 1, 'name': 'Linear Asset', 'parent_id': -1},
{'id': 2, 'name': 'Lateral', 'parent_id': 1},
{'id': 3, 'name': 'Main', 'parent_id': 1},
{'id': 4, 'name': 'Point Asset', 'parent_id': -1},
{'id': 5, 'name': 'Fountain', 'parent_id': 4},
{'id': 6, 'name': 'Hydrant', 'parent_id': 4},
{'id': 7, 'name': 'Steel', 'parent_id': 2},
{'id': 8, 'name': 'Plastic', 'parent_id': 2},
{'id': 9, 'name': 'Steel', 'parent_id': 3},
{'id': 10, 'name': 'Plastic', 'parent_id': 3}
]
应导致以下结果:
id name parent_id flat_name
0 1 Linear Asset -1 Linear Asset
1 2 Lateral 1 Linear Asset : Lateral
2 3 Main 1 Linear Asset : Main
3 4 Point Asset -1 Point Asset
4 5 Fountain 4 Point Asset : Fountain
5 6 Hydrant 4 Point Asset : Hydrant
6 7 Steel 2 Linear Asset : Lateral : Steel
7 8 Plastic 2 Linear Asset : Lateral : Plastic
8 9 Steel 3 Linear Asset : Main : Steel
9 10 Plastic 3 Linear Asset : Main : Plastic
解决方案
apply
这是一个用于完成此任务的递归函数。该函数接受id
并返回其通过树的“路径”:
def flatname(ID):
row = df[df['id'] == ID].squeeze()
if row['parent_id'] == -1:
return row['name']
else:
return flatname(row['parent_id']) + ' : ' + row['name']
要使用,请致电:
df['flat_name'] = df['id'].apply(flatname)
在df
您的第二个示例中使用了 after:
id name parent_id flat_name
0 1 Linear Asset -1 Linear Asset
1 2 Lateral 1 Linear Asset : Lateral
2 3 Main 1 Linear Asset : Main
3 4 Point Asset -1 Point Asset
4 5 Fountain 4 Point Asset : Fountain
5 6 Hydrant 4 Point Asset : Hydrant
6 7 Steel 2 Linear Asset : Lateral : Steel
7 8 Plastic 2 Linear Asset : Lateral : Plastic
8 9 Steel 3 Linear Asset : Main : Steel
9 10 Plastic 3 Linear Asset : Main : Plastic
OP 指出,上述函数明确引用df
了在函数范围之外定义的变量。因此,如果您将 DataFrame 称为不同的名称,或者您想在许多 DataFrame 上调用它,这可能会导致问题。一种解决方法是将apply
函数变成更多的私有助手,并创建一个调用它的外部(更用户友好)函数:
def _flatname_recurse(ID, df):
row = df[df['id'] == ID].squeeze()
if row['parent_id'] == -1:
return row['name']
else:
return _flatname_recurse(row['parent_id'], df=df) + ' : ' + row['name']
# asset_df to specify we are looking for a specific kind of df
def flatnames(asset_df):
return asset_df['id'].apply(_flatname_recurse, df=asset_df)
然后调用:
df['flat_name'] = flatnames(df)
另外,请注意,我曾经row = df.iloc[ID - 1, :]
用于标识行,在这种情况下有效,但取决于id
比索引大一。 这种方法更通用。
推荐阅读
- python - 如何使用py-substrate-interface获取Polkadot中所有账户的账户数据
- assembly - 谁将进程的数据加载到数据路径中的寄存器库中?
- node.js - 请求正文用空格替换加号
- salesforce - ContentVersion 上的 Salesforce DataLoader 错误
- javascript - 如何更改快速 GET 响应中的字段名称?
- autodesk-forge - Autodesk Forge App(无法获取本地颁发者证书)
- angular - Azure Pipeline Angular 项目失败
- css - 使用相对定位垂直定位背景图像
- javascript - 是否有更有效的方法来检查数组中的字符串是否存在于多个其他数组中,然后返回表示字符串存在的数组?
- c# - 为什么这个测试告诉我该值为空?