python - Python:将数据映射到层次结构

假设我有一个将子级地址映射到更宏观级地址的数据框:

Child Child's Level Parent Parent's Level
Pivet Drive Street Little Whinging Town
Little Whinging Town England Country
England Country Europe Continent
State Street Street New York City
New York City USA Country
USA Country North America Continent

我有第二个数据框,列出了每个人的地址,但该地址可能会在不同的层级中说明

Name Address Continent?
Adam Pivet Drive
Mary New York
Dave State Street

如何使用 python 填充第二个数据框中的大陆列?

一种天真的方法是将第一个数据帧转换为字典并重复向上映射,或者只是重复合并两个数据帧。但是,一旦两个数据帧都有数百万行,这并不能很好地扩展,特别是因为每条记录都不是从层次结构中的同一级别开始的。

我以前使用图形数据库 (Neo4j) 填充了大陆列,但我似乎无法在谷歌上找到任何关于如何使用 python 来执行此操作的提示。

回答1

Graph DB就是为处理这种情况而生的,如果你想在relational-db/dataframe下处理(它们是一样的),你就无法避免有很多外连接的查询。这里隐藏的概念是如何在关系数据库中 store 和检索树状数据结构。您可以将数据框视为 db 中的 table 。

这里我使用Union-Find算法来处理这个问题,注意我没有使用除Continet之外的其他级别信息,如果两个Continets包含不同级别或同一级别下的同名地点,这可能是一个错误.以下代码只是一些演示想法,但它适用于您提供的演示数据,可能不适用于您的整个数据集:

import pandas as pd
from collections import defaultdict

df = pd.DataFrame({'Child': ['Pivet Drive', 'Little Whinging', 'England', 'State Street', 'New York', 'USA'],
                   "ChildLevel": ['Street', 'Town', 'Country', 'Street', 'City', 'Country'],
                   "Parent": ['Little Whinging', 'England', 'Europe', 'New York', 'USA', 'North America'],
                   "ParentLevel": ['Town', 'Country', 'Continent', 'City', 'Country', 'Continent']})

df_to_fill = pd.DataFrame({
    'Name': ['Adam', 'Mary', 'Dave'],
    'Address': ['Pivet Drive', 'New York', 'State Street'],
})

child_parent_value_pairs = df[["Child", "Parent"]].values.tolist()

tree = lambda: defaultdict(tree)
G = tree()
for child, parent in child_parent_value_pairs:
    G[child][parent] = 1
    G[parent][child] = 1

E = [(G[u][v], u, v) for u in G for v in G[u]]
T = set()
C = {u: u for u in G}  # C stands for components
R = {u: 0 for u in G}


def find(C, u):
    if C[u] != u:
        C[u] = find(C, C[u])  # Path compression
    return C[u]


def union(C, R, u, v):
    u = find(C, u)
    v = find(C, v)
    if R[u] > R[v]:
        C[v] = u
    else:
        C[u] = v

    if R[u] == R[v]:
        R[v] += 1


for __, u, v in sorted(E):
    if find(C, u) != find(C, v):
        T.add((u, v))
        union(C, R, u, v)

all_continents = set(df[df['ParentLevel'] == 'Continent']['Parent'].tolist())
continent_lookup = {find(C, continent): continent for continent in all_continents}

df_to_fill['Continent'] = df_to_fill['Address'].apply(lambda x: continent_lookup.get(find(C, x), None))

print(df_to_fill)

输出:

Name       Address      Continent
0  Adam   Pivet Drive         Europe
1  Mary      New York  North America
2  Dave  State Street  North America

相似文章

随机推荐

最新文章