python – pandas DataFrame – 如何对行进行分组和标记

我有一大堆数据,我想提取两列,我设法使用下面的代码:

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的行被标记为文件的名称,以便我可以为每个标记执行binning?

作为旁注,在定义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]

拿起那些Exp. m / z值落在ppm差异(150 ppm)内,仍然与plistcollect的格式相同.然后我用np.digitize进行binning:

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

但是,我想规范化EACH数据文件的强度值,所以我认为应该对每个文件的单独数据进行分箱.因此,为什么我要问是否有一种方法可以为每个相应的文件标记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对象.

EDIT2:
pickuplist.xls文件包含一个名为“mass”的列,它只有一个Exp列表.我用来拾取获得的Exp的m / z值.来自附加文件的m / z值(其中ppm 150进来,因此那些Exp.m / z值落在150 ppm差异内(abs(质量 – 质量来自文件)/质量* 1000000 = 150).leferlist.xls看起来像:

mass
1013.34
1079.3757
1095.3706
1136.3972
1241.4285
1257.4234

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

最佳答案 我想你可以在
concat中使用参数键:

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

编辑:

我想我明白了.需要标记列首先添加到匹配,然后通过带有标记列的np.digitize进行groupby:

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
点赞