【问题标题】:create hierarchy using two columns in pandas使用熊猫中的两列创建层次结构
【发布时间】:2019-09-20 05:54:16
【问题描述】:

我正在使用的数据如下:

Name RefSecondary     RefMain
test  2               3   
bet   3               4   
get   1               2   
set   null            1   
net   3               5

我做了一个非常简单的查询,它查找数据框中值的存在并构建层次结构

sys_role = 'sample.xlsx'
df = pd.read_excel(sys_role,na_filter = False).apply(lambda x: x.astype(str).str.strip())
for i in range(count):
    for j in range(count):
        if df.iloc[i]['RefMain'] == df.iloc[j]['RefSecondary']:
            df.iloc[j, df.columns.get_loc('Name')] = "/".join([df.iloc[i]['Name'],df.iloc[j]['Name']])
    j = j+1
i = i+1

我得到的结果如下:

   Result          RefMain
0  get/test           3
1  test/bet           4
2  set/get            2
3  set                1
4  test/net           5

这真的很慢,逻辑也不能完美运行。有没有办法让我更快地完成这项工作?

逻辑需要如下:

 1)Take a value from column RefMain,and find its correspoding RefSecondary value.  
 2)Look up the RefSecondary value  in RefMain, 
 3)If found Back to Step 1 and repeat.
 4)This continues recursively till no value/null is found in RefSecondary column.

结果数据框应如下所示:

   Result            RefMain
0  set/get/test          3
1  set/get/test/bet      4
2  set/get               2
3  set                   1
4  set/get/test/net      5

【问题讨论】:

  • 问题不见了?
  • 更新澄清
  • 你的逻辑还不清楚,你能解释一下你是如何得到第二行的 RefMain=4
  • RefMain 4,对应的 RefSecondary 值为 3。现在 3 可以在 RefMain Column 中找到,其对应的 RefSecondary 为 2。现在 2 可以在 RefMain Column 中找到,其 RefSecondary 为 1。现在 1可以在 RefMain Column 中找到,其 RefSecondary 为空或不匹配。 SInce 不匹配,因此流停止并且所有值都被加起来。

标签: python pandas dataframe hierarchy


【解决方案1】:

这听起来像是一个图表问题。你可以试试networkx如下:

df = df.fillna(-1)

# create a graph
G = nx.DiGraph()

# add reference as edges
G.add_edges_from(zip(df['RefMain'],df['RefSecondary'] ))

# rename the nodes accordingly
G = nx.relabel_nodes(G, mapping=df.set_index('RefMain')['Name'].to_dict())


# merge the path list to the dataframe
df = df.merge(pd.DataFrame(nx.shortest_path(G)).T['null'], 
              left_on='Name', 
              right_index=True)

# new column:
df['Path'] = df['null'].apply(lambda x: '/'.join(x[-2::-1]) )

输出:

   Name RefSecondary RefMain                         null              Path
0  test            2       3       [test, get, set, null]      set/get/test
1   bet            3       4  [bet, test, get, set, null]  set/get/test/bet
2   get            1       2             [get, set, null]           set/get
3   set         null       1                  [set, null]               set
4   net            3       5  [net, test, get, set, null]  set/get/test/net

【讨论】:

  • df = df.merge(pd.DataFrame(nx.shortest_path(G)).T[-1] 这个谎言显示KeyError: -1
  • 我做到了。为了清楚起见,我还编写了有关如何加载 Dataframe 的代码,以防万一。
  • 您的原始数据是nan,而不是第4行的nullnull是字符串'null'吗?另外,数字是string 还是int
  • 数字是 int ,是 null 是字符串 null
  • 在这种情况下,将[-1] 替换为['null']。见编辑。
【解决方案2】:

以下代码查找 ref(在本例中为 1),直到找不到任何行。它输出

def lookup(df, ref):
    arr_result=[]
    result = []
    row = df[df.RefMain==ref]
    while len(row)>0:
        arr_result.append(row.Name.iloc[0])
        result.append(("/".join(arr_result), row.RefMain.iloc[0]))
        row = df[df.RefSecondary == row.RefMain.iloc[0] ]

    return pd.DataFrame(result, columns=["Result", "RefMain"])

lookup(df,1)

输出

Result  RefMain
0   set 1
1   set/get 2
2   set/get/test    3
3   set/get/test/bet    4

在上面的问题中,您如何获得“set/get/test/net 5”行,是我遗漏了什么还是一个错误?

【讨论】:

  • 我已经用我到目前为止所做的事情更新了这个问题。
【解决方案3】:

您可以将RefMain 列设置为索引并使用reindex() 方法访问字符串:

# Convert 'RefSecondary' to numeric and set 'RefMain' as index
df['RefSecondary'] = pd.to_numeric(df.RefSecondary, errors='coerce')
df.set_index('RefMain', drop=False, inplace=True)

lst = [df['Name'].values]
new_df = df.copy()

# Iterate until all values in 'Name' are NaN 
while new_df['Name'].notna().any():
    new_df = df.reindex(new_df['RefSecondary'])
    lst.append(new_df['Name'].values)

您会得到以下数组列表lst

[array(['test', 'bet', 'get', 'set', 'net'], dtype=object),
 array(['get', 'test', 'set', nan, 'test'], dtype=object),
 array(['set', 'get', nan, nan, 'get'], dtype=object),
 array([nan, 'set', nan, nan, 'set'], dtype=object),
 array([nan, nan, nan, nan, nan], dtype=object)]

现在您可以加入字符串并创建一个新的 df。

result = ['/'.join(filter(np.nan.__eq__, i)) for i in zip(*lst[::-1])]
result = pd.DataFrame({'Result': result, 'RefMain': df['RefMain'].values})

最终结果:

             Result  RefMain
0      set/get/test        3
1  set/get/test/bet        4
2           set/get        2
3               set        1
4  set/get/test/net        5

【讨论】:

    【解决方案4】:

    此代码与merges 一起工作。它有点扭曲,但它应该运行得很快,因为(也许是因为)没有行迭代。

    简而言之,它会不断合并,直到所有新的RefSecondary 值都为空。

    我想它还可以进一步优化屏蔽merge 操作。

    df_ref = df.copy()
    
    df.rename(columns={'Name':'Result'},inplace=True)
    
    while not np.all(pd.isnull(df['RefSecondary'])):
        df = df.merge(df_ref,how='left',
                      left_on='RefSecondary',right_on='RefMain',
                      suffixes=['_old',''])
        mask_=pd.notnull(df['RefMain'])
        df.loc[mask_,'Result'] = df.loc[mask_,'Result']+'/'+df.loc[mask_,'Name']
        df.drop(['RefSecondary_old','RefMain_old','Name'],axis='columns',inplace=True)
    
    
    df = df[['Result']].join(df_ref['RefMain'])
    

    来源数据:

    df = pd.DataFrame(data=[['test',2,3],
                        ['bet',3,4],
                        ['get',1,2],
                        ['set','null',1],
                        ['net',3,5]], 
                  columns=['Name','RefSecondary','RefMain'])
    

    顺便说一句,这段代码假设原始数据是一致的。例如,如果链接中有一个循环,它将陷入无限循环。

    【讨论】:

    • df.loc[mask_,'Result'] = df.loc[mask_,'Result']+'/'+df.loc[mask_,'index'] 这一行显示KeyError: 'Result'
    • 没有列index 在上面的代码中被重命名。 Result 也是我在输出数据框中创建的列。
    • 我根据您的 cmets 调整了我的回复。
    猜你喜欢
    • 1970-01-01
    • 2017-10-18
    • 1970-01-01
    • 2014-02-04
    • 1970-01-01
    • 2015-07-15
    • 2021-04-26
    • 2017-04-18
    • 1970-01-01
    相关资源
    最近更新 更多