【问题标题】:Longest path finding with condition有条件的最长路径查找
【发布时间】:2020-11-05 20:29:12
【问题描述】:

我正在尝试解决 Python/Pandas 中的一个问题,我认为该问题与最长路径算法密切相关。我正在使用的 DataFrame 具有以下结构:

import numpy as np 
import pandas as pd

data = {
    "cusID": ["001", "001", "001", "001", "001", "001", "002", "002", "002"],
    "start": ["A", "B", "C", "D", "A", "E", "B", "C", "D"],
    "end": ["B", "C", "D", "A", "E", "A", "C", "D", "E"]
}
df = pd.DataFrame(data)
print(df)

  cusID start end
0   001     A   B
1   001     B   C
2   001     C   D
3   001     D   A
4   001     A   E
5   001     E   A
6   002     B   C
7   002     C   D
8   002     D   E

对于每个客户,我想找出最长的不包含A的序列。例如,对于客户001,序列可以如下查看:

A -> B -> C -> D -> A -> E -> A

其中 B -> C -> D 是长度为 3 的最长序列。

我正在寻找的结果 DataFrame 如下:

  cusID longestSeq
0   001          3
1   002          4

虽然我无法编写太多代码来解决这个问题,但我的一些想法是:首先,很明显我需要将原始 DataFrame 按 cusID 分组以分别分析两个序列中的每一个。我的一个想法是应用一些函数将 DataFrame 转换为这种格式:

  cusID                       seq
0   001     [A, B, C, D, A, E, A]
1   002              [B, C, D, E]

然后分别处理每个列表,并使用某种计数器来获取排除 A 的路径的最大长度。我的问题是将该逻辑转录为代码(如果正确)。任何帮助将不胜感激。

【问题讨论】:

  • 如果中间有一个循环会怎样?例如,如果您将 (D, C) 添加到第二组?
  • @DaniMesejo 如果您更改数据,使第二组的最后一个条目变为 (D, C),那么最长序列的大小仍应为 4。

标签: python pandas algorithm dataframe


【解决方案1】:

第一步是标准化序列。

seqs = pd.concat([
    df.drop(columns="end").rename(columns={"start":"node"}),
    df.groupby("cusID").tail(1).drop(columns="start").rename(columns={"end":"node"})
])
seqs = seqs.sort_values("cusID", kind="mergesort").reset_index(drop=True)

>>> seqs
   cusID node
0    001    A
1    001    B
2    001    C
3    001    D
4    001    A
5    001    E
6    001    A
7    002    B
8    002    C
9    002    D
10   002    E

然后,使用zero_runs 我们定义:

def longest_non_a(seq):
    eqa = seq == "A"
    runs = zero_runs(eqa)
    return (runs[:,1] - runs[:,0]).max()
    
result = seqs.groupby("cusID")["node"].apply(longest_non_a)

>>> result
cusID
001    3
002    4
Name: node, dtype: int64

【讨论】:

  • 我花了整个下午才完全了解您的解决方案。但这对我来说是一个很好的练习。在这个过程中我学到了很多东西。谢谢!
【解决方案2】:

由于这是一个图形问题,我建议您使用networkx

import networkx as nx

data = {
    "cusID": ["001", "001", "001", "001", "001", "001", "002", "002", "002"],
    "start": ["A", "B", "C", "D", "A", "E", "B", "C", "D"],
    "end": ["B", "C", "D", "A", "E", "A", "C", "D", "E"]
}

df = pd.DataFrame(data)


def longest_path(d):
    # create graph from edge list
    dg = nx.convert_matrix.from_pandas_edgelist(d, source="start", target="end", create_using=nx.DiGraph)

    # remove "A" if exists 
    if "A" in dg.nodes:
        dg.remove_node("A")

    # compute the longest path in the graph
    return len(nx.dag.dag_longest_path(dg))


# group-by and compute the longest path
result = df.groupby("cusID").apply(longest_path).reset_index()

print(result)

输出

  cusID  0
0   001  3
1   002  4

【讨论】:

  • 这行不通,因为 networkx 会假设如果说 B 出现两次,它们是同一个节点。
  • 例如如果您更改数据以使最后一个条目变为 D -> B 而不是 D -> Enetworkx 将抱怨存在循环。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-07
  • 2016-09-02
  • 1970-01-01
  • 2018-10-10
  • 1970-01-01
  • 2018-10-11
相关资源
最近更新 更多