【问题标题】:pandas DataFrame - how to group and tag rowspandas DataFrame - 如何对行进行分组和标记
【发布时间】:2017-06-17 14:03:00
【问题描述】:

我有大量数据要提取两列,我使用下面的代码设法做到了:

import pandas as pd
import numpy as np
import os


pickupfile = 'pickuplist.xls'

path = os.chdir('some path')
files = os.listdir(path)
files_xls = [f for f in files if f[-3:] == 'xls']

df = pd.DataFrame()
pl = pd.ExcelFile(pickupfile)
pickuplist = pd.read_excel(pl)

df = [pd.read_excel(f, 'Sheet1')[['Exp. m/z','Intensity']] for f in files_xls]

plistcollect = pd.concat(df, keys=files_xls)\
                 .reset_index(level=1, drop=True)\
                 .rename_axis('Tag')\
                 .reset_index()

pk list 文件夹中的每个文件包含 10 列,上面的代码从文件中提取两列到 plistcollect 数据帧中。对我来说不利的是文件拉取迭代将数据附加到先前数据的底部。数据如下:

Number    Exp. m/z    Intensity
1         1013.33     1000
2         1257.52     2000

以此类推,并带有附加:

Number    Exp. m/z    Intensity
1         1013.33     1000
2         1257.52     2000
3         1013.35     3000
4         1257.61     4000

其中 1~2 来自第一个文件,3~4 来自第二个文件,依此类推。每个文件都有不同数量的行或索引(即文件 1 有 400 行,文件 2 有 501 行等),这导致我的代码出现一些问题。所以问题是,有没有办法标记每个文件,以便在迭代文件以追加到 plistcollect 时,用文件名标记 plistcollect DataFrame 的行,以便我可以为每个文件执行分箱标签?


附带说明,在定义 plistcollect 之后,我执行匹配:

ppm = 150

matches = pd.DataFrame(index=pickuplist['mass'], columns=plistcollect.set_index(list(plistcollect.columns)).index, dtype=bool)

for index, findex, exp_mass, intensity in plistcollect.itertuples():
    matches[findex, exp_mass] = abs(matches.index - exp_mass) / matches.index < ppm / 1e6


results = {i: list(s.index[s]) for i, s in matches.iterrows()}
results2 = {key for key, value in matches.any().iteritems() if value}
results3 = matches.any().reset_index()[matches.any().values]

其中拾取那些经验。 m/z 值在 ppm 差异 (150 ppm) 内,仍与 plistcollect 格式相同。然后我通过以下方式使用 np.digitize 进行分箱:

bins = np.arange(900, 3000, 1)

groups = results3.groupby(np.digitize(results3['Exp. m/z'], bins))


stdev = groups['Intensity'].std()
average = groups['Intensity'].mean()
CV = stdev/average*100



resulttable = pd.concat([groups['Exp. m/z'].mean(),average,CV], axis=1)


resulttable.columns.values[1] = 'Average'
resulttable.columns.values[2] = 'CV'


resulttable.to_excel('test.xls', index=False)

这为我提供了我想要的原始数据分析(请注意,此表的数字与上面的示例表不对应):

Exp. m/z    Average     CV
1013.32693  582361.5354 13.49241757
1257.435414 494927.0904 12.45206038

但是,我想对每个数据文件的强度值进行标准化,所以我认为应该对每个文件的单独数据进行分箱。因此,为什么我要问是否有一种方法可以针对每个相应的文件标记 plistcollect 的行。另请注意,匹配过程必须在规范化之前完成。归一化是将每个强度值除以来自同一数据文件的强度值之和。使用上面的示例表,1013.33 的归一化强度为:1000/(1000+2000),1013.35 的归一化强度为:3000/(3000+4000)。

我可以毫无问题地计算每个 bin 中所有值的总和,但我似乎无法找到一种方法来找到对应于附加文件中值的来源的 bin 之间的强度值总和.

编辑:

我编辑了代码以反映答案,并将“findex”添加到匹配数据框。现在 results3 数据框似乎包含文件名作为标签。组数据框似乎也有标签值。问题是,如何按标签名称指定/分组?

filetags = groups['Tag']
resulttable = pd.concat([filetags, groups['Exp. m/z'].mean(), average, CV], axis=1)

产生以下错误消息:无法连接非 NDFrame 对象。

编辑2: Picklist.xls 文件包含一个名为“mass”的列,其中包含一个 Exp 列表。我用来拾取获得的 Exp 的 m/z 值。附加文件中的 m/z 值(其中 ppm 150 出现,因此那些 Exp. m/z 值在 150 ppm 差异范围内(abs(mass - mass_from_file)/mass*1000000 = 150)。pickuplist.xls 看起来像:

mass
1013.34
1079.3757
1095.3706
1136.3972
1241.4285
1257.4234

这些就是我所说的已知拾取列表,每个文件可能包含也可能不包含这些质量值。匹配定义实际上也来自 Stack Overflow 的一位用户。它用于迭代 plistcollect,并选择那些 Exp。与“质量”相差 150 ppm 以内的 m/z 值。

【问题讨论】:

  • resulttable = pd.concat([filetags, groups['Exp. m/z'].mean(), average, CV], axis=1) 之前是print (type(filetags)) 吗?
  • 它说 pandas.core.groupby.SeriesGroupBy。结果表后的 print(filetags) 不会产生错误,因此我将文件标签连接到结果表的方式一定有问题。
  • 我认为你错过了一些聚合函数,例如 groups['Tag'].mean()groups['Tag'].std()
  • hmm 标签值是字符串值,例如 C09.xls - 文件名。
  • 好的,我想帮助您使用另一个代码,但是从 ppm = 150results3 = matches.any().reset_index()[matches.any().values] 的代码不明白。另外我没有数据表单列mass...你能解释更多并添加一些数据样本吗?

标签: python pandas


【解决方案1】:

我认为你可以在concat 中使用参数keys

dfs = []
for f in files_xls:
    dfs = pd.read_excel(f, 'Sheet1')[['Exp. m/z','Intensity']]
    dfs.append(data)

同理:

dfs = [pd.read_excel(f, 'Sheet1')[['Exp. m/z','Intensity']] for f in files_xls]

plistcollect = pd.concat(dfs, keys=files_xls) \
                 .reset_index(level=1, drop=True) \
                 .rename_axis('Tag') \
                 .reset_index()
print (plistcollect)
         Tag  Exp.m/z  Intensity
0  test1.xls  1013.33       1000
1  test1.xls  1257.52       2000
2  test2.xls  1013.35       3000
3  test2.xls  1257.61       4000

编辑:

我想我明白了。需要Tag 列首先添加到匹配项,然后由np.digitizeTag 列分组:

print (plist)
         Tag  Exp. m/z  Intensity
0  test1.xls      1000       2000
1  test1.xls      1000       1500
2  test1.xls      2000       3000
3  test2.xls      3000       4000
4  test2.xls      4000       5000
5  test2.xls      4000       5500

pickup = pd.DataFrame({'mass':[1000,1200,1300, 4000]})
print (pickup)
   mass
0  1000
1  1200
2  1300
3  4000

matches = pd.DataFrame(index=pickup['mass'], 
                       columns = plist.set_index(list(plist.columns)).index, 
                       dtype=bool)

ppm = 150
for index, tags, exp_mass, intensity in plist.itertuples():
    matches[(tags, exp_mass)] = abs(matches.index - exp_mass) / matches.index < ppm / 1e6

print (matches)
Tag       test1.xls               test2.xls              
Exp. m/z       1000          2000      3000   4000       
Intensity      2000   1500   3000      4000   5000   5500
mass                                                     
1000           True   True  False     False  False  False
1200          False  False  False     False  False  False
1300          False  False  False     False  False  False
4000          False  False  False     False   True   True

results3 = matches.any().reset_index(name='a')[matches.any().values]
print (results3)
         Tag  Exp. m/z  Intensity     a
0  test1.xls      1000       2000  True
1  test1.xls      1000       1500  True
4  test2.xls      4000       5000  True
5  test2.xls      4000       5500  True

bins = np.arange(900, 3000, 1)
groups = results3.groupby([np.digitize(results3['Exp. m/z'], bins), 'Tag'])

resulttable = groups.agg({'Intensity':['mean','std'], 'Exp. m/z': 'mean'})
resulttable.columns = resulttable.columns.map('_'.join)
resulttable['CV'] = resulttable['Intensity_std'] / resulttable['Intensity_mean'] * 100
d = {'Intensity_mean':'Average','Exp. m/z_mean':'Exp. m/z'}
resulttable = resulttable.reset_index().rename(columns=d) \
                          .drop(['Intensity_std', 'level_0'],axis=1)
print (resulttable)
         Tag  Average  Exp. m/z         CV
0  test1.xls     1750      1000  20.203051
1  test2.xls     5250      4000   6.734350

【讨论】:

  • plistcollect 行似乎产生了一个错误,指出在 .reset_index(level=1, drop=True) 处出现意外缩进。所以我从 pd.concat(dfs, keys=files_xls) 中删除了 ')' 并在末尾添加了右括号 (.reset_index()),这现在给我一个错误消息列表对象没有属性 'reset_index' .
  • 你可以添加``或者创建一个长行,然后就可以了。
  • 非常感谢您的回答。当我试图找出解决方案时,你能想出这段代码真是太神奇了。无论如何,这似乎按预期按标签对 results3 数据帧进行分组。然后,当制作结果表时,似乎没有计算平均值、标准差和 CV。另外,我需要将每个文件的每个元素(强度值)除以匹配后的强度值之和。因此,对于 test1.xls,我将对强度值求和,然后将每个强度除以总和。有没有办法这样做?如果我要求太多,我深表歉意。
  • 我尝试删除组 groupby 步骤(包含 np.digitize)的“标签”部分,然后进行了计算,但现在标签已按预期消失。
  • 很高兴能帮上忙。我有想法。我们可以通过电子邮件进行交流吗?我的电子邮件在我的个人资料中。
猜你喜欢
  • 2014-05-15
  • 2022-08-15
  • 2013-12-06
  • 2020-12-06
  • 1970-01-01
  • 2015-09-09
  • 2020-11-16
  • 1970-01-01
  • 2021-07-20
相关资源
最近更新 更多