熊猫数据框到嵌套计数器字典

时间:2018-10-22 19:59:24

标签: python pandas dataframe counter defaultdict

我已经看到了很多有关如何将熊猫数据帧转换为嵌套字典的问题,但是这些问题都不涉及信息的汇总。我什至可以在大熊猫中做我需要的事情,但是我被困住了。

输入

我有一个看起来像这样的数据框:

  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列,以返回一个元组(位置,字母,计数),在“ 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)]

我尝试过的

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

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

consensus = TGXXT

2 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:2)

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

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'