シーケンス関係に基づいてデータフレーム列をグループ化する方法


8

2つの列間のシーケンス関係に基づいてグループ化しようとしています。

d = {'df1':[10,20, 30, 60, 70, 40, 30, 70], 'df2':[20, 30, 40, 80, 70, 50, 90, 100]}

df = pd.DataFrame(data = d)
df

   df1  df2
0   10  20
1   20  30
2   30  40
3   60  80
4   80  70
5   40  50
6   30  90
7   70  100

私は以下のような結果を期待しています:

より明確にするために:-df1とdf2には、シーケンスに基づいた関係があります。たとえば、10は20と直接関係にあり、10は30から20に間接関係にあります。また、10は40から20と30に間接関係にあります。追加の例として、80が70に直接関係し、 100から70までの間接的な関係。これは、残りの列値に対して機能します。

  df1  |    df2
  -----|-------------------
0   10 | 20, 30, 40, 50, 90
1   20 | 30, 40, 50, 90
2   30 | 40, 50, 90
3   60 | 80, 70, 100
4   80 | 70, 100
5   40 | 50
6   70 | 100

以下のスクリプトを使用しようとしていますが、成功できませんでした。

(df.groupby('df1')
   .agg({ 'df2' : ','.join})
   .reset_index()
   .reindex(columns=df.columns))

誰かがこの挑戦を手伝ってくれる?ここスタックオーバーフローで同様の解決策がある場合は、親切に私に知らせてください。

編集: 最初の答えは上記の例で完全に機能しますが、実行したいデータを試してみると、正しく機能しません。私の実際のデータは以下のようになります。

    df1 df2
0   10  20
1   10  30
2   10  80
3   10  90
4   10  120
5   10  140
6   10  170
7   20  180
8   30  40
9   30  165
10  30  175
11  40  20
12  40  50
13  50  60
14  60  70
15  70  180
16  80  180
17  90  100
18  100 110
19  110 180
20  120 130
21  130 180
22  140 150
23  150 160
24  160 165
25  165 180
26  165 200
27  170 175
28  175 180
29  175 200
30  180 190
31  190 200
32  200 210
33  210 220
34  220 230
35  230 240
36  240 -

1
こんにちは、グループ化する列間の関係を明確にできますか?
eva-vw

1
こんにちはエヴァ、返信ありがとうございます。df1とdf2には、シーケンスに基づいた関係があります。たとえば、10は20と直接関係にあり、10は30から20に間接関係にあります。また、10は40から20と30に間接関係にあります。追加の例として、80が70に直接関係し、 100から70までの間接的な関係。これは、残りの列の値に対して機能します。
資本論

最初の行のシーケンスに90があるのはなぜですか?最初の列に50はないので、シーケンスはそこで終了するはずです。多分私は何かを誤解しました。
トレスコフ

@treskov返信ありがとうございます。インデックス番号6を見るとわかるように、30は90と直接関係にあります。10は30から20に間接関係にあることがわかります。つまり、10は90から30に間接関係にあります。これは一種の推移的であると言えますプロパティがそれだけではありません。
資本論

回答:


3

考えられる解決策の1つ:

import pandas as pd
from itertools import chain

l1 = [10, 20, 30, 60, 80, 40, 30, 70]
l2 = [20, 30, 40, 80, 70, 50, 90, 100]

d = dict()
for i, j in zip(l1, l2):
    if i == j:
        continue
    d.setdefault(i, []).append(j)

for k in d:
    d[k].extend(chain.from_iterable(d.get(v, []) for v in d[k]))

df = pd.DataFrame({'df1': list(d.keys()), 'df2': [', '.join(str(v) for v in d[k]) for k in d]})
print(df)

プリント:

   df1                 df2
0   10  20, 30, 40, 90, 50
1   20      30, 40, 90, 50
2   30          40, 90, 50
3   60         80, 70, 100
4   80             70, 100
5   40                  50
6   70                 100

編集:新しい入力データに基づく他のソリューション。今、私はパスの可能な円をチェックしています:

import pandas as pd

data = '''
0   10  20
1   10  30
2   10  80
3   10  90
4   10  120
5   10  140
6   10  170
7   20  180
8   30  40
9   30  165
10  30  175
11  40  20
12  40  50
13  50  60
14  60  70
15  70  180
16  80  180
17  90  100
18  100 110
19  110 180
20  120 130
21  130 180
22  140 150
23  150 160
24  160 165
25  165 180
26  165 200
27  170 175
28  175 180
29  175 200
30  180 190
31  190 200
32  200 210
33  210 220
34  220 230
35  230 240
36  240 -
'''

df1, df2 = [], []
for line in data.splitlines()[:-1]: # <--- get rid of last `-` character
    line = line.strip().split()
    if not line:
        continue

    df1.append(int(line[1]))
    df2.append(int(line[2]))

from pprint import pprint

d = dict()
for i, j in zip(df1, df2):
    if i == j:
        continue
    d.setdefault(i, []).append(j)

for k in d:
    seen = set()
    for v in d[k]:
        for val in d.get(v, []):
            if val not in seen:
                seen.add(val)
                d[k].append(val)


df = pd.DataFrame({'df1': list(d.keys()), 'df2': [', '.join(str(v) for v in d[k]) for k in d]})
print(df)

プリント:

    df1                                                df2
0    10  20, 30, 80, 90, 120, 140, 170, 180, 40, 165, 1...
1    20                  180, 190, 200, 210, 220, 230, 240
2    30  40, 165, 175, 20, 50, 180, 200, 190, 210, 220,...
3    40  20, 50, 180, 190, 200, 210, 220, 230, 240, 60, 70
4    50          60, 70, 180, 190, 200, 210, 220, 230, 240
5    60              70, 180, 190, 200, 210, 220, 230, 240
6    70                  180, 190, 200, 210, 220, 230, 240
7    80                  180, 190, 200, 210, 220, 230, 240
8    90        100, 110, 180, 190, 200, 210, 220, 230, 240
9   100             110, 180, 190, 200, 210, 220, 230, 240
10  110                  180, 190, 200, 210, 220, 230, 240
11  120             130, 180, 190, 200, 210, 220, 230, 240
12  130                  180, 190, 200, 210, 220, 230, 240
13  140   150, 160, 165, 180, 200, 190, 210, 220, 230, 240
14  150        160, 165, 180, 200, 190, 210, 220, 230, 240
15  160             165, 180, 200, 190, 210, 220, 230, 240
16  165             180, 200, 190, 210, 200, 220, 230, 240
17  170             175, 180, 200, 190, 210, 220, 230, 240
18  175             180, 200, 190, 210, 200, 220, 230, 240
19  180                       190, 200, 210, 220, 230, 240
20  190                            200, 210, 220, 230, 240
21  200                                 210, 220, 230, 240
22  210                                      220, 230, 240
23  220                                           230, 240
24  230                                                240

またはpprint(d, width=250)

{10: [20, 30, 80, 90, 120, 140, 170, 180, 40, 165, 175, 100, 130, 150, 190, 20, 50, 200, 110, 160, 60, 210, 70, 220, 230, 240],
 20: [180, 190, 200, 210, 220, 230, 240],
 30: [40, 165, 175, 20, 50, 180, 200, 190, 210, 220, 230, 240, 60, 70],
 40: [20, 50, 180, 190, 200, 210, 220, 230, 240, 60, 70],
 50: [60, 70, 180, 190, 200, 210, 220, 230, 240],
 60: [70, 180, 190, 200, 210, 220, 230, 240],
 70: [180, 190, 200, 210, 220, 230, 240],
 80: [180, 190, 200, 210, 220, 230, 240],
 90: [100, 110, 180, 190, 200, 210, 220, 230, 240],
 100: [110, 180, 190, 200, 210, 220, 230, 240],
 110: [180, 190, 200, 210, 220, 230, 240],
 120: [130, 180, 190, 200, 210, 220, 230, 240],
 130: [180, 190, 200, 210, 220, 230, 240],
 140: [150, 160, 165, 180, 200, 190, 210, 220, 230, 240],
 150: [160, 165, 180, 200, 190, 210, 220, 230, 240],
 160: [165, 180, 200, 190, 210, 220, 230, 240],
 165: [180, 200, 190, 210, 200, 220, 230, 240],
 170: [175, 180, 200, 190, 210, 220, 230, 240],
 175: [180, 200, 190, 210, 200, 220, 230, 240],
 180: [190, 200, 210, 220, 230, 240],
 190: [200, 210, 220, 230, 240],
 200: [210, 220, 230, 240],
 210: [220, 230, 240],
 220: [230, 240],
 230: [240]}

編集2:df「df1」および「df2」列を持つ入力データフレームの場合:

from pprint import pprint

d = dict()
for i, j in zip(df.df1, df.df2):
    if i == j:
        continue
    if j == '-':   # <-- this will remove the `-` character in df2
        continue
    d.setdefault(i, []).append(j)

for k in d:
    seen = set()
    for v in d[k]:
        for val in d.get(v, []):
            if val not in seen:
                seen.add(val)
                d[k].append(val)


df = pd.DataFrame({'df1': list(d.keys()), 'df2': [', '.join(str(v) for v in d[k]) for k in d]})
print(df)

d[k].extend(chain.from_iterable(d.get(v, []) for v in d[k]))これがどのように機能するか説明できますか?私はその文書を見たが、ついていけなかった。
sathyz

@sathyz私はイテラブルをchain.from_iterableフラット化するために使用しています-この場合、イテラブルはディクショナリのリストd(またはキーvd-に存在しない場合は空のリストd.get(v, []))で構成されています。次に、これらの値を使用して、に格納されているリストを拡張しd[k]ます。
Andrej Kesely

@AndrejKesely if not (line := line.strip().split()):は言うことif not (line != line.strip().split()):ですか?または、他の何か。でエラーが発生し:ます。作成すると、行でエラー!=IndexError: string index out of range 発生しますdf1.append(int(line[1]))
Kapital

1
@AndrejKeselyパーフェクト。どうもありがとうございました!
Kapital

1
@Kapitalあなたは何か間違ったことをしています、(あなたの質問に投稿された)データからの結果は私の最初の編集と同じです。Jupyter Notebookを使用していますか?はいの場合は、リロード/再起動してください...更新されたコードでこの結果を取得する方法はありません。
Andrej Kesely

1

説明をありがとうございます。再試行機能を備えた解決策があります。大きなデータフレームでは効率的ではないかもしれませんが、うまく機能しているようです。この関数はリストを返しますが、結果の系列を編集して、リストを文字列に結合することもできます。

def get_related(df1, related):
    # get directly related values
    next_vals = df.loc[df['df1'] == df1, 'df2'].values.tolist()
    # remove links to self (will cause recursion issues)
    next_vals = list(set(next_vals) - set([df1]))
    # add to running list
    related = related + next_vals
    # continue to next level
    if any(next_val in df['df1'].unique() for next_val in next_vals):
        for next_val in next_vals:
            related = related + get_related(next_val, related)
    # get unique list
    return list(set(related))

df['df1'].apply(lambda x: get_related(x, []))

関数の引数から「関連する」とは何かを説明できますか?
Kapital

0

これでうまくいくはずです:

def recursive_walk(df, node):
    parents=df.loc[(df['df1']==node) & (df['df2']!=node), 'df2'].tolist()
    if(len(parents)==0):
        yield node
    else:
        for parent in parents:
            yield parent
            lst=[el for el in recursive_walk(df, parent)]
            for el in lst:
                yield el

df['tree']=df.apply(lambda x: list(set([el for el in recursive_walk(df, x['df2'])]+[x['df2']])), axis=1)

出力:

   df1  df2                  tree
0   10   20  [40, 50, 20, 90, 30]
1   20   30      [40, 50, 90, 30]
2   30   40              [40, 50]
3   60   80                  [80]
4   70   70             [100, 70]
5   40   50                  [50]
6   30   90                  [90]
7   70  100                 [100]

(*)私も拡張データフレームをチェックしました-かなり高速です、私のIDEがそれを切り捨てているので、出力を共有しません;)


返信が遅くなって申し訳ありませんが、私はあなたの解決策を見ていましたが、node(関数のパラメーターの1つ)が何を意味するのか理解できませんでしたか?教えてくださいますか?
Kapital

node現在の値です。したがって、それを返し、それ自体とは異なる親(循環参照と呼ばれる)がある場合は、その親を反復処理し、それらに対して同じ関数を実行します。
Grzegorz Skibinski

ノードに乱数を与えるだけで、次のような結果が得られます。<generator object recursive_walk at 0x0000022A67551D48>。どういう意味かわかりません。
Kapital

試してみてください:list(recursive_walk(...))または[el for el in recursive_walk(...)]関数の戻り値generator-つまり、基本的には、すべての要素がeg listまたはのように一度にではなく、tupleすべての値を1つずつ返すために使用できる反復可能を提供します。
Grzegorz Skibinski
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.