#python #pandas
#python #pandas
Вопрос:
Я не уверен, что формулировка заголовка оптимальна, потому что проблему, с которой я столкнулся, немного сложно объяснить. В коде у меня есть df, который выглядит примерно так:
import pandas as pd
import numpy as np
a = ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'D', 'E', 'E']
b = [3, 1, 2, 3, 12, 4, 7, 8, 3, 10, 12]
df = pd.DataFrame([a, b]).T
df
Выдает
0 1
0 A 3
1 A 1
2 A 2
3 B 3
4 B 12
5 B 4
6 C 7
7 C 8
8 D 3
9 E 10
10 E 12
Мне известны методы groupby для группировки по значениям в столбце, но это не совсем то, что я хочу. Я вроде как хочу сделать шаг дальше, когда любое пересечение в столбце 1 между группами столбца 0 группируется вместе. Моя формулировка ужасна (вероятно, поэтому у меня возникают проблемы с вводом этого в код), но вот в основном то, что я хочу в качестве вывода:
0 1
0 A-B-D-E 3
1 A-B-D-E 1
2 A-B-D-E 2
3 A-B-D-E 3
4 A-B-D-E 12
5 A-B-D-E 4
6 C 7
7 C 8
8 A-B-D-E 3
9 A-B-D-E 10
10 A-B-D-E 12
По сути, A, B и D имеют общее значение 3 в столбце 1, поэтому их метки группируются вместе в столбце 0. Теперь, поскольку B и E имеют общее значение 12 в столбце 1, а B разделяет значение 3 в столбце 1 с A и D, E также группируется с A, B и D . Единственное значение в столбце 0, которое осталось независимым, — это C, поскольку оно не пересекается ни с какой другой группой.
В моей голове это заканчивается рекурсивным циклом, но, похоже, я не могу понять точную логику. Буду признателен за любую помощь.
Комментарии:
1. Обратите внимание, что A, B, C, D являются произвольными представлениями значений в 0. Мне не нужно, чтобы имя было в каком-либо алфавитном порядке или что-либо еще.
Ответ №1:
Если кто-нибудь в будущем столкнется с тем же, это сработает (хотя, возможно, это не лучшее решение в мире):
import pandas as pd
import numpy as np
a = ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'D', 'E', 'E']
b = ['3', '1', '2', '3', '12', '4', '7', '8', '3', '10', '12']
df = pd.DataFrame([a, b]).T
df.columns = 'a', 'b'
df2 = df.copy()
def flatten(container):
for i in container:
if isinstance(i, (list,tuple)):
for j in flatten(i):
yield j
else:
yield i
bad = True
i =1
while bad:
print("Round " str(i))
i = i 1
len_checker = []
for variant in list(set(df.a)):
eGenes = list(set(df.loc[df.a==variant, 'b']))
inter_variants = []
for gene in eGenes:
inter_variants.append(list(set(df.loc[df.b==gene, 'a'])))
if type(inter_variants[0]) is not str:
inter_variants = [x for x in flatten(inter_variants)]
inter_variants = list(set(inter_variants))
len_checker.append(inter_variants)
if len(inter_variants) != 1:
df2.loc[df2.a.isin(inter_variants),'a']='-'.join(inter_variants)
good_checker = max([len(x) for x in len_checker])
df['a'] = df2.a
if good_checker == 1:
bad=False
df.a = df.a.apply(lambda x: '-'.join(list(set(x.split('-')))))
df.drop_duplicates(inplace=True)
Ответ №2:
Следующее создает желаемый результат без рекурсий. Я не тестировал это с другими созвездиями (другой порядок, больше комбинаций и т.д.).
a = ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'D', 'E', 'E']
b = [3, 1, 2, 3, 12, 4, 7, 8, 3, 10, 12]
df = list(zip(a, b))
print(df)
class Bucket:
def __init__(self, keys, values):
self.keys = set(keys)
self.values = set(values)
def contains_key(self, key):
return key in self.keys
def add_if_contained(self, key, value):
if value in self.values:
self.keys.add(key)
return True
elif key in self.keys:
self.values.add(value)
return True
return False
def merge(self, bucket):
self.keys.update(bucket.keys)
self.values.update(bucket.values)
def __str__(self):
return f'{self.keys} :: {self.values}>'
def __repr__(self):
return str(self)
res = []
for tup in df:
added = False
if res:
selected_bucket = None
remove_idx = None
for idx, bucket in enumerate(res):
if not added:
added = bucket.add_if_contained(tup[0], tup[1])
selected_bucket = bucket
elif bucket.contains_key(tup[0]):
selected_bucket.merge(bucket)
remove_idx = idx
if remove_idx is not None:
res.pop(remove_idx)
if not added:
res.append(Bucket({tup[0]}, {tup[1]}))
print(res)
Генерирует следующий вывод:
$ python test.py
[('A', 3), ('A', 1), ('A', 2), ('B', 3), ('B', 12), ('B', 4), ('C', 7), ('C', 8), ('D', 3), ('E', 10), ('E', 12)]
[{'B', 'D', 'A', 'E'} :: {1, 2, 3, 4, 10, 12}>, {'C'} :: {8, 7}>]