Pandas 数据框到嵌套计数器字典

Pandas dataframe to nested counter dictionary

我见过很多关于如何将 pandas 数据帧转换为嵌套字典的问题,但其中 none 涉及聚合信息。我什至可以在 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}])

问题

我需要将每一行的目标列拆分为 return 元组(位置、字母、计数),其中起始位置在 "pos" 列中给出,然后枚举后面每个位置的字符串,计数是在 "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)]

我试过的

我创建了将目标列分解为找到的位置的代码,return创建一个位置元组、核苷酸(字母)并计算该字母,并将它们作为一列添加到数据框:

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)

其中 return 是基于该行的目标列的每一行的元组列表。

我需要为每个目标获取 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 代表找到联系的位置,没有一个字母可以是 return编辑为最大值:

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

不确定如何获得所需的输出,但我创建了列表 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')

这可能不是最优雅的解决方案,但我认为它可能会满足您的需求:

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'