Почему использование списка в качестве значения в строке pandas позволяет мне обновляться на лету? - PullRequest
0 голосов
/ 14 апреля 2020

У меня есть поведение, которое я не понимаю. Было бы замечательно, если бы кто-то мог объяснить, почему это работает, а также показать мне более хороший способ сделать это, это стало очень замысловатым.

Моя цель здесь - объединить генетические c варианты, которые находятся рядом с каждым другой на данной хромосоме. Это должно найти любую комбинацию вариантов длиной до 5 б.п. (позиция). Это означает, что если есть 5 позиций рядом друг с другом, все они должны быть объединены, и тогда эти позиции не должны проверяться снова. Затем то же самое для комбинаций длиной 4, 3, 2. Я обрабатываю эту строку с помощью этой строки 'для var_len в [5, 4, 3, 2, 1]:' и набора уже_популярных. Это решение работает, но только если я добавлю «tumour_alts» в список в моей df. Это почему? И может ли кто-нибудь показать мне, как получить тот же вывод, не прибегая к итерации?

df = pd.DataFrame([['chr1',13,['A']],
              ['chr1',5,['A']],
              ['chr1',6,['G']],
              ['chr2',9,['G']],
               ['chr1',4,['C']],
              ['chr1',11,['T']]],
              columns=['chrom','pos','tumour_alts'],
             index=['chr1:13','chr1:5','chr1:6','chr2:9','chr1:4','chr1:11'])
already_updated = set([])
for chrom, df_tmp in df.groupby('chrom'):
    df_tmp = df_tmp.sort_values(by=['pos'])
    for var_len in [5, 4, 3, 2, 1]:
        df_tmp['dif'] = df_tmp.pos.diff(var_len)
        hits=df_tmp[df_tmp['dif'] == var_len]
        for hit in hits.pos:
            rows = df_tmp[(df_tmp.pos <= hit) & (df_tmp.pos >= (hit - var_len))]
            update = dict(rows.iloc[0])
            for i in range(var_len):
                i+=1
                update_tmp = dict(rows.iloc[i])
                key = update_tmp.get('chrom') + ':'+str(update_tmp.get('pos'))
                if key not in already_updated:
                    df = df.drop(index=(key))
                    update['tumour_alts'][0]+=update_tmp.get('tumour_alts')[0]
                    already_updated.add(key)


df

chrom   pos tumour_alts
chr1:13 chr1    13  [A]
chr2:9  chr2    9   [G]
chr1:4  chr1    4   [CAG]
chr1:11 chr1    11  [T]

Редактировать: я увеличил сложность, чтобы лучше описать проблему.

Учитывая:

df = pd.DataFrame([['chr1',13,['A']],
                   ['chr1',3,['A']],
                  ['chr1',5,['A']],
                  ['chr1',6,['G']],
                  ['chr2',9,['G']],
                   ['chr1',4,['C']],
                  ['chr1',11,['T']],
                  ['chr1',55,['A']],
                  ['chr1',56,['G']],
                  ['chr2',95,['G']],],
                  columns=['chrom','pos','tumour_alts'],
                 index=['chr1:13','chr1:3','chr1:5','chr1:6','chr2:9','chr1:4','chr1:11','chr1:55','chr1:56','chr1:95'])
df = df.sort_values('pos').sort_values('chrom')

Я ожидаю:

chrom   pos tumour_alts
chr1:3  chr1    3   [ACAG]
chr1:11 chr1    11  [T]
chr1:13 chr1    13  [A]
chr1:55 chr1    55  [AG]
chr2:9  chr2    9   [G]
chr2:95 chr2    95  [G]

1 Ответ

2 голосов
/ 14 апреля 2020

Вы можете попробовать использовать groupby, а не for-l oop. Кроме того, я не уверен, что вы подразумеваете под «Это должно найти любую комбинацию вариантов длиной до 5 б.п. (позиция)», поэтому я не включил это в приведенный ниже код.

# remove characters from list
df['tumour_alts'] = df['tumour_alts'].astype(str).str.replace("\[|\]", '').str.replace("'", '')
# sort values
df = df.sort_values('pos')
# groupby chrom (assuming you need to group these together)
g = df.groupby('chrom')['pos']
# check the value above and below to see if they are == eachother
mask = ~((g.shift(0) == g.shift(-1)-1) | (g.shift(0) == g.shift(1)+1))
# use cumsum to assign a number value for each group
gr = mask.cumsum()-mask.cumsum().where(~mask).ffill().fillna(0).astype(int)
# groupby gr and transform with sum to append strings together
s = df.groupby(gr)['tumour_alts'].transform(sum).drop_duplicates(keep='first').to_frame()
# drop column and merge right
df.drop(columns='tumour_alts').merge(s, left_index=True, right_index=True, how='right')

        chrom  pos tumour_alts
chr1:4   chr1    4         CAG
chr2:9   chr2    9           G
chr1:11  chr1   11           T
chr1:13  chr1   13           A

, хотя приведенный выше код выведите ожидаемый результат Я не знаю много о хромосомах, поэтому drop_duplicates может быть неуместным. Вы можете объединить все вместе, а затем отбросить строки на основе соответствующих параметров.

# remove characters from list
df['tumour_alts'] = df['tumour_alts'].astype(str).str.replace("\[|\]", '').str.replace("'", '')
# sort values
df = df.sort_values('pos')
# groupby chrom (assuming you need to group these together)
g = df.groupby('chrom')['pos']
# check the value above and below to see if the are == eachother
mask = ~((g.shift(0) == g.shift(-1)-1) | (g.shift(0) == g.shift(1)+1))
# use cumsum to assign a number value for each group
gr = mask.cumsum()-mask.cumsum().where(~mask).ffill().fillna(0).astype(int)
# groupby gr and transform with sum to append strings together
s = df.groupby(gr)['tumour_alts'].transform(sum).to_frame()
# merge right
df.merge(s, left_index=True, right_index=True, how='right')

        chrom  pos tumour_alts_x tumour_alts_y
chr1:4   chr1    4             C           CAG
chr1:5   chr1    5             A           CAG
chr1:6   chr1    6             G           CAG
chr2:9   chr2    9             G             G
chr1:11  chr1   11             T             T
chr1:13  chr1   13             A             A
...