python - 组合/平均多个数据文件

标签 python bash

我有一组数据文件(例如,“data####.dat”,其中 #### = 0001,...,9999),它们都具有相同 x 的通用数据结构- 第一列中的值,然后是具有不同 y 值的多个列。

data0001.dat:

#A < comment line with unique identifier 'A'
#B1 < this is a comment line that can/should be dropped
1 11 21
2 12 22
3 13 23

data0002.dat:

#A < comment line with unique identifier 'A'
#B2 < this is a comment line that can/should be dropped
1 13 23
2 12 22
3 11 21

它们基本上源 self 的程序使用不同种子的不同运行,我现在想将这些部分结果组合成一个公共(public)直方图,以便保留以“#A”开头的注释行(所有文件都相同)和其他评论行被删除。第一列保留,然后所有其他列应该对所有数据文件进行平均:

dataComb.dat:

#A < comment line with unique identifier 'A'
1 12 22 
2 12 22 
3 12 22 

其中 12 = (11+13)/2 = (12+12)/2 = (13+11)/222 = (21+23)/2 = (22+22)/2 = (23+21)/2

我已经有一个 bash 脚本(可能是可怕的代码;但我不是很有经验......)通过运行 ./merge.sh data* > dataComb.dat 来完成这项工作命令行。它还检查所有数据文件是否具有相同的列数和第一列中的相同值。

merge.sh:

#!/bin/bash

if [ $# -lt 2 ]; then
    echo "at least two files please"
    exit 1;
fi

i=1
for file in "$@"; do
    cols[$i]=$(awk '
BEGIN {cols=0}
$1 !~ /^#/ {
  if (cols==0) {cols=NF}
  else {
    if (cols!=NF) {cols=-1}
  }
}
END {print cols}
' ${file})
    i=$((${i}+1))
done

ncol=${cols[1]}
for i in ${cols[@]}; do
    if [ $i -ne $ncol ]; then
        echo "mismatch in the number of columns"
        exit 1
    fi
done

echo "#combined $# files"
grep "^#A" $1

paste "$@" | awk "
\$1 !~ /^#/ && NF>0 {
  flag=0
  x=\$1
  for (c=1; c<${ncol}; c++) { y[c]=0. }
  i=1
  while (i <= NF) {
    if (\$i==x) {
      for (c=1; c<${ncol}; c++) { y[c] += \$(i+c) }
      i+= ${ncol}
    } else { flag=1; i=NF+1; }
  }
  if (flag==0) {
    printf(\"%e \", x)
    for (c=1; c<${ncol}; c++) { printf(\"%e \", y[c]/$#) }
    printf(\"\n\")
  } else { printf(\"# x -coordinate mismatch\n\") }
}"

exit 0

我的问题是,对于大量数据文件,它会很快变慢,有时会抛出“打开的文件太多”错误。我看到简单地一次性粘贴所有数据文件(paste "$@")是这里的问题,但分批进行并以某种方式引入临时文件似乎也不是理想的解决方案。如果能在保留脚本调用方式的同时使它更具可扩展性,我将不胜感激,即所有数据文件作为命令行参数传递

我决定也将它发布到 python 部分,因为我经常被告知处理此类问题非常方便。然而,我几乎没有使用 python 的经验,但也许这是最终开始学习它的机会 ;)

最佳答案

下面附加的代码适用于 Python 3.3 并产生所需的输出,但有一些小警告:

  • 它从它处理的第一个文件中获取初始注释行,但不会费心检查之后的所有其他文件是否仍然匹配(即,如果您有多个以#A 开头的文件和一个以#A 开头的文件以 #C 开头,它不会拒绝 #C,即使它可能应该)。我主要想说明 merge 函数在 Python 中是如何工作的,并且认为添加这种类型的杂项有效性检查最好留作“家庭作业”问题。
  • 它也不会费心检查行数和列数是否匹配,如果不匹配可能会崩溃。将其视为另一个小作业问题。
  • 它将第一列右侧的所有列打印为浮点值,因为在某些情况下,它们可能就是这样。初始列被视为标签或行号,因此打印为整数值。

你可以像以前一样调用代码;例如,如果您将脚本文件命名为 merge.py,您可以执行 python merge.py data0001.dat data0002.dat,它会将合并的平均结果打印到标准输出,就像使用 bash 脚本一样。与早期的答案之一相比,该代码还具有更大的灵 active :它的编写方式原则上应该(我还没有实际测试以确保)能够合并具有任意数量列的文件,而不仅仅是恰好有三列的文件。另一个好处是:它不会在处理完文件后保持文件打开状态; with open(name, 'r') as infile: 行是一个 Python 习惯用法,即使 close() 从未被显式调用。

#!/usr/bin/env python

import argparse
import re

# Give help description
parser = argparse.ArgumentParser(description='Merge some data files')
# Add to help description
parser.add_argument('fname', metavar='f', nargs='+',
                    help='Names of files to be merged')
# Parse the input arguments!
args = parser.parse_args()
argdct = vars(args)

topcomment=None
output = {}
# Loop over file names
for name in argdct['fname']:
    with open(name, "r") as infile:
        # Loop over lines in each file
        for line in infile:
            line = str(line)
            # Skip comment lines, except to take note of first one that
            # matches "#A"
            if re.search('^#', line):
                if re.search('^#A', line) != None and topcomment==None:
                    topcomment = line
                continue
            items = line.split()
            # If a line matching this one has been encountered in a previous
            # file, add the column values
            currkey = float(items[0])
            if currkey in output.keys():
                for ii in range(len(output[currkey])):
                    output[currkey][ii] += float(items[ii+1])
            # Otherwise, add a new key to the output and create the columns
            else:
                output[currkey] = list(map(float, items[1:]))

# Print the comment line
print(topcomment, end='')
# Get total number of files for calculating average
nfile = len(argdct['fname'])              
# Sort the output keys
skey = sorted(output.keys())
# Loop through sorted keys and print each averaged column to stdout
for key in skey:
    outline = str(int(key))
    for item in output[key]:
        outline += ' ' + str(item/nfile)
    outline += '\n'
    print(outline, end='')

关于python - 组合/平均多个数据文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20818522/

相关文章:

python - 我的路由中需要什么 base_name 参数才能使这个 Django API 工作?

python - 在 CPU 上运行 tf.slim 评估循环

python - 在 Python 2.x 中转换 u'\xe 0' to '\u00E0'?

python - 从所有字典值中获取第 n 个元素 - Python

regex - 捕获两个标记之间的文本

linux - 无法读取输出以运行下一个命令

python - 什么时候在 python 中使用 double datetime.datetime ?

perl - Perl 中的 bash 导出等效于什么?

bash - 如何检查文件的大小是否大于 Bash 中的某个值

linux - 如何在 Mac 上使用命令行终端将 .txt 文件转换为 .vcf 格式?