Làm cách nào để nhóm các cột khung dữ liệu dựa trên mối quan hệ chuỗi của chúng


8

Tôi đang cố gắng nhóm dựa trên mối quan hệ trình tự của họ giữa hai cột.

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

Tôi đang mong đợi kết quả gì đó bên dưới:

Để làm cho nó rõ ràng hơn: - df1 và df2 có mối quan hệ dựa trên trình tự của chúng. Ví dụ: 10 có mối quan hệ trực tiếp với 20 và 10 có mối quan hệ gián tiếp với 30 đến 20. Và 10 cũng có mối quan hệ gián tiếp với 40 đến 20 và 30. Một ví dụ khác, chúng ta hãy lấy 80 có quan hệ trực tiếp với 70 và quan hệ gián tiếp với 100 đến 70. Điều này hoạt động cho phần còn lại của các giá trị cột.

  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

Tôi đang thử sử dụng đoạn script bên dưới nhưng tôi không thể thành công.

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

Bất cứ ai có thể giúp đỡ về thách thức này? Nếu có bất kỳ giải pháp tương tự nào ở đây tại Stack tràn, vui lòng cho tôi biết.

Chỉnh sửa: Câu trả lời đầu tiên hoạt động hoàn hảo với ví dụ trên nhưng khi tôi thử với dữ liệu mà tôi muốn thực hiện thì nó không hoạt động chính xác. dữ liệu thực của tôi trông như dưới đây.

    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
Xin chào, bạn có thể làm rõ mối quan hệ giữa các cột mà bạn muốn nhóm không?
eva-vw

1
Xin chào Eva, Cảm ơn bạn đã trả lời của bạn. df1 và df2 có mối quan hệ dựa trên trình tự của chúng. Ví dụ: 10 có mối quan hệ trực tiếp với 20 và 10 có mối quan hệ gián tiếp với 30 đến 20. Và 10 cũng có mối quan hệ gián tiếp với 40 đến 20 và 30. Một ví dụ khác, chúng ta hãy lấy 80 có quan hệ trực tiếp với 70 và quan hệ gián tiếp với 100 đến 70. Điều này hoạt động cho phần còn lại của các giá trị cột.
Kapital

Tại sao có 90 trong chuỗi trong hàng đầu tiên? Không có 50 trong cột đầu tiên nên chuỗi sẽ kết thúc ngay tại đó. Có lẽ tôi đã hiểu nhầm điều gì đó.
treskov

@treskov Cảm ơn bạn đã trả lời. Như bạn thấy trên chỉ số số 6, 30 có mối quan hệ trực tiếp với 90. và chúng ta biết rằng 10 có mối quan hệ gián tiếp với 30 đến 20. Vì vậy, 10 có mối quan hệ gián tiếp với 90 đến 30. Chúng ta có thể nói đây là một dạng chuyển tiếp tài sản nhưng nó là nhiều hơn thế.
Kapital

Câu trả lời:


3

Một giải pháp khả thi:

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)

Bản in:

   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

EDIT: Giải pháp khác dựa trên dữ liệu đầu vào mới. Bây giờ tôi đang kiểm tra các vòng tròn có thể có trong đường dẫn:

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)

Bản in:

    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

Hoặc 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]}

EDIT 2: Nếu dflà khung dữ liệu đầu vào của bạn với các cột "df1" và "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)

Bạn có thể giải thích làm thế nào d[k].extend(chain.from_iterable(d.get(v, []) for v in d[k]))điều này hoạt động? Tôi nhìn vào tài liệu, nhưng không thể làm theo.
sathyz

@sathyz Tôi sử dụng chain.from_iterableđể làm phẳng iterable - trong trường hợp này, iterable bao gồm các danh sách từ từ điển d(hoặc danh sách trống, nếu khóa vkhông tồn tại trong d- d.get(v, [])). Sau đó, tôi sử dụng các giá trị này để mở rộng danh sách được lưu trữ tại d[k].
Andrej Kesely 23/12/19

@AndrejKesely if not (line := line.strip().split()):là để nói if not (line != line.strip().split()):? hoặc một cái gì đó khác. Tôi nhận được lỗi với :. Khi tôi làm điều đó, !=tôi nhận được IndexError: string index out of range lỗi tại dòng df1.append(int(line[1])).
Kapital

1
@AndrejKesely Hoàn hảo. Cảm ơn ông rất nhiều!!
Kapital

1
@Kapital Bạn đang làm gì đó sai, kết quả từ dữ liệu (như được đăng trong câu hỏi của bạn) giống như trong EDIT đầu tiên của tôi. Bạn có sử dụng máy tính xách tay Jupyter? Nếu có, hãy tải lại / khởi động lại nó ... Không có cách nào để có được kết quả này với mã được cập nhật của tôi.
Andrej Kesely

1

Xin chào cảm ơn đã làm rõ, tôi có một giải pháp với chức năng đệ quy mà bạn có thể thử. Có thể không hiệu quả cho các khung dữ liệu lớn nhưng dường như hoạt động tốt. Hàm trả về một danh sách nhưng bạn có thể chỉnh sửa chuỗi kết quả để nối danh sách thành một chuỗi theo cách bạn muốn.

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, []))

Bạn có thể giải thích "liên quan" là gì từ đối số chức năng?
Kapital

0

Cái này cần phải dùng mẹo:

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)

Đầu ra:

   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]

(*) Tôi cũng đã kiểm tra với khung dữ liệu mở rộng - khá nhanh, tôi sẽ không chia sẻ đầu ra, vì IDE của tôi đang cắt bớt nó;)


Xin lỗi vì trả lời trễ, nhưng tôi đã xem xét giải pháp của bạn và tôi không thể hiểu ý của bạn là gì node(một trong các tham số của bạn trong hàm)? Bạn có thể nói cho tôi biết?
Kapital

nodelà giá trị bạn hiện đang ở. Vì vậy, bạn trả lại nó, và trong trường hợp nó có bất kỳ cha mẹ nào, khác với chính nó (được gọi là tham chiếu vòng tròn), bạn lặp lại trên cha mẹ của nó và chạy cùng chức năng cho chúng.
Grzegorz Skibinski

Khi tôi chỉ đưa ra một số ngẫu nhiên cho nút, tôi sẽ nhận được một cái gì đó như thế này. <generator object recursive_walk at 0x0000022A67551D48>. Đó là tôi không biết ý nghĩa của nó.
Kapital

Hãy thử: list(recursive_walk(...))hoặc [el for el in recursive_walk(...)]trả về hàm generator- về cơ bản có nghĩa là - không phải tất cả các phần tử cùng một lúc như ví dụ listhoặc tuplenhưng cung cấp khả năng lặp mà bạn có thể sử dụng để trả về tất cả các giá trị từng cái một.
Grzegorz Skibinski
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.