Это можно решить с помощью теории графов анализа. Похоже, вы хотите получить все преемников , начиная с каждого из узлов в col2
. Для этого нам нужно сначала построить ориентированный граф , используя столбцы col1
и col2
. Для этого мы можем использовать networkX и построить nx.DiGraph
из кадра данных, используя nx.from_pandas_edgelist
:
import networkx as nx
m = df.ne('').all(1)
G = nx.from_pandas_edgelist(df[m],
source='col1',
target='col2',
create_using=nx.DiGraph())
И затем мы можем перебрать nodes
в col2
и поиск всех преемников, начиная с этого узла. Для этого мы можем использовать dfs_tree
, который будет проходить по графику в поисках преемников с первым поиском по глубине из источника:
all_successors = [list(nx.dfs_tree(G, node)) for node in df.loc[m,'col2']]
Теперь мы можем присвоить обратно список самых длинных путей с:
out = (df.assign(
**pd.DataFrame(all_successors, index=df[m].index)
.reindex(df.index)
.fillna('')
.add_prefix('new_col')))
print(out)
col1 col2 new_col0 new_col1 new_col2 new_col3
0 basic c c c++ java python
1 c c++ c++ java python
2 c++ java java python
3 ruby
4 php
5 java python python
6 python
7 r
8 c
Чтобы лучше объяснить этот подход, рассмотрим вместо этого слегка отличную сеть с дополнительным компонентом:
Как уже упоминалось, здесь мы хотим получить список преемников для каждого из узлов, которые у нас есть в Col2
. Для этих задач существует несколько алгоритмов поиска графа, которые можно использовать для изучения ветвей графа, начиная с данного узла. Для этого мы можем использовать функции depth first search
, доступные в nx.algorithms.traversal
. В этом случае нам нужен nx.dfs_tree
, который возвращает ориентированное дерево , построенное посредством поиска в глубину, начиная с указанного узла.
Вот несколько примеров :
list(nx.dfs_tree(G, 'c++'))
# ['c++', 'java', 'python', 'julia']
list(nx.dfs_tree(G, 'python'))
# ['python', 'julia']
list(nx.dfs_tree(G, 'basic'))
# ['basic', 'php', 'sql']
Обратите внимание, что в случае наличия циклов внутри графика это может оказаться довольно сложным. Скажем, есть, например, грань между c++
и scala
. В этом случае становится неясно, какой путь выбрать. Одним из способов может быть обход всех соответствующих путей с помощью nx.dfs_tree
и сохранение одного интересующего пути, предопределяющего некоторые логики c, например, сохранение самого длинного. Хотя, похоже, дело не в этой проблеме.