将值列表添加到行,然后将数据框转换为长格式

Posted

技术标签:

【中文标题】将值列表添加到行,然后将数据框转换为长格式【英文标题】:Adding list of values to rows, turning the dataframe into long format afterwards 【发布时间】:2015-07-12 08:48:33 【问题描述】:

我有一个数据框,其中索引是基因组位置,值是 p 值:

import pandas as pd
from StringIO import StringIO
from collections import defaultdict

data = """Pos   MedialIIvsD  LateralIIvsD  MedialP02IIvsD  MedialP09IIvsD
chr1_-_12200      0.557431      0.066554        0.738343        0.029935
chr1_-_12600      0.737887      0.069167        0.829568        0.409495
chr1_-_48400      0.349833      0.600912        0.964103        0.765195
chr1_-_172800     0.729035      0.035198        0.866111        0.385711"""

df = pd.read_csv(StringIO(data), sep='\s+', index_col=False, 
                 header=False)

此外,我还有一个将不同基因组位置映射到基因的字典:

pos_to_gene = defaultdict(list, "chr1_-_12200": ["GENE1"],
                                 "chr1_-_12600": ["GENE1", "GENE2"],
                                 "chr1_-_172800": ["GENE3"])

我想要实现的是在此数据框中添加 Gene 作为列。这对我来说并不简单,因为一个基因组位置可能有多个基因:

pd.Series(df.index.values).apply(lambda pos: pos_to_gene[pos])
0           [GENE1]
1    [GENE1, GENE2]
2                []
3           [GENE3]
dtype: object

如何实现这样的长格式输出数据帧?

Gene   Pos   MedialIIvsD  LateralIIvsD  MedialP02IIvsD  MedialP09IIvsD
GENE1  chr1_-_12200      0.557431      0.066554        0.738343        0.029935
GENE1  chr1_-_12600      0.737887      0.069167        0.829568        0.409495
GENE2  chr1_-_12600      0.737887      0.069167        0.829568        0.409495
NaN    chr1_-_48400      0.349833      0.600912        0.964103        0.765195
GENE3  chr1_-_172800     0.729035      0.035198        0.866111        0.385711

【问题讨论】:

我迫切需要一个更好的标题。不胜感激。 这可能会有所帮助:***.com/questions/26068021/… 谢谢!将不得不调查,希望它不是完全重复。 它们 90% 相似,但在我的情况下还需要几行。谢谢! 【参考方案1】:

应用我在answer here 中学到的知识:

df.insert(0, "Gene", df.Pos.apply(lambda pos: pos_to_gene[pos]))

def expand(row):
    genes = row['Gene']
    s = pd.Series(row['Pos'], index=list(set(genes)))
    return s

sdf = df.apply(expand, axis=1).stack()

返回

0  GENE1     chr1_-_12200
1  GENE1     chr1_-_12600
   GENE2     chr1_-_12600
3  GENE3    chr1_-_172800
dtype: object

非常接近;缺少的只是其余的数据。

现在剩下的就是合并堆叠数据框 (sdf) 和原始数据框 (df)。

sdf = sdf.to_frame().reset_index(level=1, drop=False)
sdf.columns = ["Gene", "Pos"]
pd.merge(sdf, df, left_on = 'Pos', right_on = 'Pos')

就是这样!

    Gene            Pos  MedialIIvsD  LateralIIvsD  MedialP02IIvsD  \
0  GENE1   chr1_-_12200     0.557431      0.066554        0.738343
1  GENE1   chr1_-_12600     0.737887      0.069167        0.829568
2  GENE2   chr1_-_12600     0.737887      0.069167        0.829568
3  GENE3  chr1_-_172800     0.729035      0.035198        0.866111

   MedialP09IIvsD
0        0.029935
1        0.409495
2        0.409495
3        0.385711

也许有更聪明的方法来做到这一点。

【讨论】:

【参考方案2】:

这里有一个技巧可以使用 pd.Series(1, index=...) 并让 pandas 对齐:

In [11]: s = df["Pos"].apply(lambda x: pd.Series(1, pos_to_gene[x])).stack(0)

In [12]: s
Out[12]:
0  GENE1    1
1  GENE1    1
   GENE2    1
3  GENE3    1
dtype: float64

您可以重置索引,然后简单地加入:

In [13]: s.index.names = [None, "Gene"]

In [14]: gene = s.reset_index("Gene")[["Gene"]]

In [15]: gene
Out[15]:
    Gene
0  GENE1
1  GENE1
1  GENE2
3  GENE3

In [16]: gene.join(df)
Out[16]:
    Gene            Pos  MedialIIvsD  LateralIIvsD  MedialP02IIvsD  MedialP09IIvsD
0  GENE1   chr1_-_12200     0.557431      0.066554        0.738343        0.029935
1  GENE1   chr1_-_12600     0.737887      0.069167        0.829568        0.409495
1  GENE2   chr1_-_12600     0.737887      0.069167        0.829568        0.409495
3  GENE3  chr1_-_172800     0.729035      0.035198        0.866111        0.385711

如果您想包含 NaN 行(不在您的答案中),那么外部联接:

In [17]: gene.join(df, how="outer")
Out[17]:
    Gene            Pos  MedialIIvsD  LateralIIvsD  MedialP02IIvsD  MedialP09IIvsD
0  GENE1   chr1_-_12200     0.557431      0.066554        0.738343        0.029935
1  GENE1   chr1_-_12600     0.737887      0.069167        0.829568        0.409495
1  GENE2   chr1_-_12600     0.737887      0.069167        0.829568        0.409495
2    NaN   chr1_-_48400     0.349833      0.600912        0.964103        0.765195
3  GENE3  chr1_-_172800     0.729035      0.035198        0.866111        0.385711

作为替代方案,您可以在纯 python 中创建 gene(而不是使用 apply):

inds, gens = [], []
for i, p in df["Pos"].iteritems():
    for g in pos_to_gene[p]:
        inds.append(i)
        gens.append(g)
gene = pd.Series(gens, inds)

【讨论】:

注意:对 Pos 列使用分类也可能会有所帮助,如果有很多重复,可能可以节省一些工作... 谢谢,我的版本非常慢。将报告这是如何工作的。 没有重复,但会记住另一次。顺便说一句,感谢您的 git 书。 @TheUnfunCat 哦,你是读者! :p 我必须发布一些最近的编辑,谢谢你提醒我!! @您应该尝试将其添加到 goodreads.com,以便人们可以传播福音。将给出至少 4/5 的完成版本。让草稿版本像 (braveclojure.com) 一样开放可能是获得大量良好反馈/口碑的好方法。

以上是关于将值列表添加到行,然后将数据框转换为长格式的主要内容,如果未能解决你的问题,请参考以下文章

PySimpleGui:如何将值从一个列表框添加到另一个列表框

DataFrame和列表的笛卡尔积

如何将值列表附加到数据框中的列表列

如何将长格式的数据框转换为适当格式的列表?

将值添加到数据框和导出

将 Pandas 数据框转换为包含 ID 和权重的元组列表