Pandas dataframe для словаря вложенных счетчиков - PullRequest
0 голосов
/ 22 октября 2018

Я видел много вопросов о том, как преобразовать кадры данных pandas во вложенные словари, но ни один из них не занимается агрегацией информации.Я даже могу делать то, что мне нужно в пандах, но я застрял.

Ввод

У меня есть кадр данных, который выглядит следующим образом:

  FeatureID    gene  Target  pos  bc_count
0     1_1_1  NRAS_3  TAGCAC    0      0.42
1     1_1_1  NRAS_3  TGCACA    1      1.00
2     1_1_1  NRAS_3  GCACAA    2      0.50
3     1_1_1  NRAS_3  CACAAA    3      2.00
4     1_1_1  NRAS_3  CAGAAA    3      0.42

# create df as below
import pandas as pd
df = pd.DataFrame([{"FeatureID":"1_1_1", "gene":"NRAS_3", "Target":"TAGCAC", 
   "pos":0, "bc_count":.42},
   {"FeatureID":"1_1_1", "gene":"NRAS_3", "Target":"TGCACA", "pos":1, 
   "bc_count":1.00},
   {"FeatureID":"1_1_1", "gene":"NRAS_3", "Target":"GCACAA", "pos":2, 
   "bc_count":0.50},
   {"FeatureID":"1_1_1", "gene":"NRAS_3", "Target":"CACAAA", "pos":3, 
   "bc_count":2.00},
   {"FeatureID":"1_1_1", "gene":"NRAS_3", "Target":"CAGAAA", "pos":4, 
   "bc_count":0.42}])

Проблема

Мне нужно разбить столбец Target для каждой строкивернуть кортеж (position, letter, count), где начальная позиция указана в столбце pos, а затем перечислить строку для каждой следующей позиции, а count - это значение, найденное для этой строки в bc_countстолбец

Например, в первой строке желаемый список кортежей будет выглядеть следующим образом:

[(0, "T", 0.42), (1,"A", 0.42), (2,"G", 0.42), (3,"C", 0.42), (4,"A", 0.42), (5,"C", 0.42)]

Что я пробовал

Я создал код, который разбиваетсяцелевой столбец в найденной позиции, возвращая кортеж позиции, нуклеотид (букву) и счет для этой буквы, и добавляет их в виде столбца к фрейму данных:

def index_target(row):
    count_list = [((row.pos + x),y, 
        row.bc_count) for x,y in 
        enumerate(row.Target)]

df['pos_count'] = df.apply(self.index_target, axis=1)

, который возвращает список кортежей длякаждая строка основана на целевом столбце этой строки.

Мне нужно взять каждую строку в df для каждой цели и суммировать значения.Вот почему я подумал об использовании словаря в качестве счетчика:

position[letter] += bc_count

Я попытался создать defaultdict, но он добавляет каждый список кортежей отдельно, а не суммирует значения для каждой позиции:

from collections import defaultdict

d = defaultdict(dict) # also tried defaultdict(list) here
for x,y,z in row.pos_count:
    d[x][y] += z

Требуемый вывод

Для каждого объекта в кадре данных, где числа ниже представляют сумму отдельных подсчетов, найденных в столбце bc_count для каждой позиции, а x представляет позиции, где были найдены связи, ини одно письмо не может быть возвращено в качестве максимального:

pos A   T   G   C
0   25  80  25  57
1   32  19  100 32
2   27  18  16  27
3   90  90  90  90
4   10  42  37  18

консенсус = TGXXT

Ответы [ 2 ]

0 голосов
/ 23 октября 2018

Возможно, это не самое элегантное решение, но я думаю, что оно может выполнить то, что вам нужно:

new_df = pd.DataFrame(
    df.apply(
        # this lambda is basically the same thing you're doing,
        # but we create a pd.Series with it
        lambda row: pd.Series(
            [(row.pos + i, c, row.bc_count) for i, c in enumerate(row.Target)]
        ),
        axis=1)
        .stack().tolist(),
    columns=["pos", "nucl", "count"]

)

Где new_df выглядит так:

  pos nucl count
0   0    T  0.42
1   1    A  0.42
2   2    G  0.42
3   3    C  0.42
4   4    A  0.42
5   5    C  0.42
6   1    T  1.00
7   2    G  1.00
8   3    C  1.00
9   4    A  1.00

Затем я бы повернул это, чтобы получить агрегированные значения:

nucleotide_count_by_pos = new_df.pivot_table(
    index="pos",
    columns="nucl",
    values="count",
    aggfunc="sum",
    fill_value=0
)

Где nucleotide_count_by_pos выглядит так:

nucl     A     C     G     T
 pos
   0  0.00  0.00  0.00  0.42
   1  0.42  0.00  0.00  1.00
   2  0.00  0.00  1.92  0.00
   3  0.00  4.34  0.00  0.00
   4  4.34  0.00  0.00  0.00

А затем, чтобы получить консенсус:

def get_consensus(row):
    max_value = row.max()
    nuc = row.idxmax()
    if (row == max_value).sum() == 1:
        return nuc
   else:
        return "X"

consensus = ''.join(nucleotide_count_by_pos.apply(get_consensus, axis=1).tolist())

Что в случае данных вашего примера будет:

'TTGCACAAA'
0 голосов
/ 23 октября 2018

Не уверен, как получить желаемый результат, но я создал список d, который содержит кортежи, которые вы хотели для фрейма данных.Надеемся, что это дает некоторое направление в том, что вы хотите создать:

d = []

for t,c,p in zip(df.Target,df.bc_count,df.pos):
    d.extend([(p,c,i) for i in list(t)])

df_new = pd.DataFrame(d, columns = ['pos','count','val'])
df_new = df_new.groupby(['pos','val']).agg({'count':'sum'}).reset_index()

df_new.pivot(index = 'pos', columns = 'val', values = 'count')
...