[英]Recursive Operation in Pandas
我有一個像這樣的數據幀:
vals = {"operator": [1, 1, 1, 2, 3, 5], "nextval": [2, 3, 6, 4, 5, 6]}
df = pd.DataFrame(vals)
operator nextval
0 1 2
1 1 3
2 1 6
3 2 4
4 3 5
5 5 6
我想要做的是使用運算符和 nextval 獲取從起點(如 1)到終點(如 6)的所有可能路徑的列表,而不是嚴格意義上的最短路徑。 輸出可以很靈活,但我正在尋找這樣的東西或傳達這一點的東西:
1 -> 6
1 -> 2 -> 4
1 -> 3 -> 5 -> 6
我能夠關閉它,但不確定如何正確地進行遞歸,因為 dict 無法處理 2 個相同的鍵:
import pandas as pd
vals = {"operator": [1, 1, 1, 2, 3, 5], "nextval": [2, 3, 6, 4, 5, 6]}
df = pd.DataFrame(vals)
df1 = df.set_index("operator")
dictvals = {}
for x in df1.index.unique():
dictvals[x] = []
df2 = df1.loc[x]
if isinstance(df2, pd.DataFrame):
for idx, rowdata in df2.iterrows():
dictvals[x].append(rowdata["nextval"])
else:
dictvals[x] = df2[0]
print(dictvals)
{1: [2, 3, 6], 2: 4, 3: 5, 5: 6}
檢查networkx
,您需要一個帶有'root'
到'leaf'
路徑的方向圖
import networkx as nx
G=nx.from_pandas_edgelist(df,source='operator',target='nextval', edge_attr=None, create_using=nx.DiGraph())
road=[]
for n in G:
if G.out_degree(n)==0: #leaf
road.append(nx.shortest_path(G, 1, n))
road
Out[82]: [[1, 2, 4], [1, 3, 5, 6]]
更新
import networkx as nx
G=nx.from_pandas_edgelist(df,source='operator',target='nextval', edge_attr=None, create_using=nx.DiGraph())
road=[]
for n in G:
if G.out_degree(n)==0: #leaf
road.append(list(nx.all_simple_paths(G, 1, n)))
road
Out[509]: [[[1, 3, 5, 6], [1, 6]], [[1, 2, 4]]]
讓我們嘗試手動滾動解決方案,因為考慮這種遞歸算法是有教育意義的。 (當然,在現實世界中只使用現有的庫是合適的;它可能會更加容錯。)
您顯示的代碼構建了圖形本身的可識別表示,但為了一致性,即使只有一個后繼節點,也最好對值使用列表(或集合或元組)。 我認為集合在這里最有意義,因為如果輸入中有重復的條目,那么我們應該丟棄重復的圖節點。 所以讓我們假設我們從以下開始:
graph = {1: {2, 3}, 2: {4}, 3: {5}, 5: {6}}
我們同意將自己限制在考慮有向無環圖上。 我建議可以遞歸地找到來自我們根節點的路徑,如下所示:遞歸地檢查來自每個后繼節點的每條路徑; 累積這些結果,並在每個結果前面加上從根到相應后繼的鏈接。
當然,當我們編寫遞歸代碼時,我們希望避免副作用,因為它們使推理變得更加困難。 因此,讓我們改為說:對於每個后繼,對於來自該后繼的每個 pat,所有路徑的累積,定義為(從節點到后繼的鏈接)+(從后繼到結束的路徑)。 當然,我們表示“從節點到后繼節點的鏈接”的方式只是當前節點名稱和一個箭頭; 我們從遞歸中得到路徑的其余部分,包括后繼名稱。
然后我們需要一個基本情況:如果沒有后繼者,那么我們有一條從這里到末尾的路徑(因為我們在末尾),這就是節點名稱本身。 如果我們的圖中的死胡同用空集表示,我們的代碼會更簡單; 但顯然省略這些鍵更容易生成圖形。 因此,當我們進行檢查時,我們將依靠dict.get
而不是索引。
嗯,第一部分對我來說聽起來很像列表理解(有兩個for
子句`。對於基本情況,為了匹配它,我們需要一個包含一個路徑的列表。這給了我們:
def paths(graph, root):
successors = graph.get(root, set())
if not successors:
return [str(root)] # a single path starting here and going nowhere.
return [
f'{root} -> {path}'
for successor in successors
for path in paths(graph, successor)
]
讓我們試試看:
>>> paths({1: {2, 3}, 2: {4}, 3: {5}, 5: {6}}, 1)
['1 -> 2 -> 4', '1 -> 3 -> 5 -> 6']
或者,您可以使用生成器表達式而不是列表推導式,甚至將其編寫為遞歸生成器(使用yield
和yield from
)。
(如果我們覺得足夠厚臉皮,我們可以使用條件表達式繼續函數式編程主題:)
def paths(graph, root):
successors = graph.get(root, set())
return [
f'{root} -> {path}'
for successor in successors
for path in paths(graph, successor)
] if successors else [str(root)]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.