计算 linux 的 gnuplot 中几个文件的列之间的平均值

Calculate average value between columns of several files within gnuplot for linux

我有一个目录列表(例如 0、50、100、150、200 等),每个目录都包含一个名为 zb_p.xy 的文件,其中包含两列数据。这些是此类文件的示例:

# file 0/zb_p.xy
1 0.1
2 0.2 
3 0.15
4 0.11

# file 50/zb_p.xy
1 0.0
2 0.4
3 0.30
4 0.1

我想从所有 zb_p.xy 文件中提取第 2 列的数据,并使用 linux 中的 gnuplot 绘制它们之间的平均值及其标准偏差。

到目前为止,这是我的尝试:

LIST = system("ls -1 */zb_p.xy*")
FILES = words(LIST)
FILE(i) = word(LIST,i)
plot for [i=1:FILES] FILE(i)

MATLAB 中的这段代码似乎可以工作,但我需要 gnuplot 中的类似代码:

D=dir('*');
[s ~]=size(D);

for i=1:s
    dirName=D(i,1).name;
    cd(dirName)     %steps into directory
    fileID=load('zb_p.xy');
    zb(:,i)=fileID(:,2);
    cd ..           %steps out of directory
end
zb_mean=mean(zb,2);
zb_std=std(zb,0,2);
errorbar(zb_mean,zb_std/sqrt(s),'sk')

您可以使用以下 bash 命令将所有文件粘贴到一个文件中:

# bash: paste filenames in directories 1, 2, and 3
paste */file.dat
 # 1/file.dat       # 2/file.dat    # 3/file.dat
 7 6                7 3             2 0
 0 4                3 4             0 3
 0 8                5 0             9 1
 2 9                5 0             2 6
 6 8                7 2             4 3

此输出可以作为临时文件(有 6 列)传递给 gnuplot,以便您可以操作要绘制的列:

# gnuplot
data = "<( paste */file.dat )"
plot data u 1:((++)/3.0) w lp pt 6 ps 2

编辑: 对于以上几个文件,列的数量可能很大。列操作可以通过 awk 自动执行。以下 awk 脚本计算每行的平均值和标准偏差,对于第 2、4、6 列,...等(假设它被称为 mean.awk):

#!/usr/bin/awk -f

# script mean.awk
{
    mean=0
    std=0

    # calculate mean 
    for(i=2; i<=NF; i+=2)    mean += $i  

    mean /= 0.5*NF 

    # calculate standard dev
    for(i=2; i<=NF; i+=2)    std += ($i-mean)*($i-mean)

    std = sqrt(std/(0.5*NF-1))

    print mean, std
}

处理数据的 bash 命令是

paste */file.dat | grep -v ^# | awk -f mean.awk
  3        3
  3.66667  0.57735
  3        4.3589
  5        4.58258
  4.33333  3.21455

其中第一列和第二列分别是平均值和标准差。 grep命令是忽略以字符#.

开头的行

最后,您可以在 gnuplot 中绘制标准差与平均值的关系图:

data = "<( paste */file.dat | grep -v ^# | awk -f mean.awk )"
plot data u 1:2 w lp pt 6 ps 2

示例(不是有史以来最好的情节):

如果你不想写 awk 脚本,这是单行命令版本:

data = "<( paste */file.dat | grep -v ^# | awk '{mean=0; std=0; for(i=2; i<=NF; i+=2) mean += $i; mean /= 0.5*NF; for(i=2; i<=NF; i+=2) std += ($i-mean)*($i-mean); std = sqrt(std/(0.5*NF-1)); print mean, std }' )"

plot data u 1:2 w lp pt 6 ps 2

由于我遇到了完全相同的问题,但对给出的答案不满意,这是我自己的版本:

我有双列测量文件,其中第一列是索引键,第二列是测量值。我的文件也有注释行。

必要前提: 每个输入文件的第 n 行必须对应于相同的测量值(所有这些值均取平均值)。带有注释的行不会被忽略!

我的解决方案使用 awk (gawk-4.2.1) 对指定为 -v COL=n 的列中的所有值求和,其中 n 是从 1 开始的列号。所以内存消耗应该与行数成正比,而不是与正在使用的文件数成正比。

诀窍是通过滥用最后一个输入文件来避免拆分和连接输出的输入字段。好了,废话不多说,来看代码:

# output a new table where column COL is the average of all input files
BEGIN {
    FILENUM = ARGC - 1
}
{
    if (ARGIND == 1) {  # first file
        SUM[NR] = 0
    }
}
NF >= COL && $COL ~ /^-?[0-9]/ {
    SUM[FNR] += $COL
}
{
    if (ARGIND == FILENUM) {    # last file
        if (FILENUM > 1 && NF >= COL && $COL ~ /^-?[0-9]/)
            $COL = SUM[FNR] / FILENUM;
        print [=10=];
    }
}

对于输入文件 input1input2input3,我使用命令

awk -f multi-column-mean.awk -v COL=2 input{1,2,3} >output

创造output。作为一个非常简单的测试 运行,请考虑以下示例数据:

输入1:

720 0.403176
730 0.399838
# Lab = 73.45771 -0.552744 -2.636218

输入2:

720 0.394166
730 0.391083
# Lab = 72.911591 -0.718176 -2.942526

输入3:

720 0.364636
730 0.361698
# Lab = 70.623329 -0.713199 -2.19574

输出:

720 0.387326
730 0.384206
# Lab = 70.623329 -0.713199 -2.19574

请注意,注释与上一个输入文件 (input3) 没有变化。

最后是一个包含我的完整数据的示例图(B1B2B3 是原始输入文件,Mean 是输出文件。最后两个值是示例中显示的值):

只有一个输入文件的情况稍微优化为“按原样”输出文件。为避免针对“零输入文件”情况(使用标准输入)发出警告 awk: multi-column-mean.awk:11: (FILENAME=- FNR=1) Warnung: reference to uninitialized element 'SUM["1"]',请将相应行替换为 if (ARGIND <= 1) { # first file or stdin

主要是肌肉,脂肪很少,希望你喜欢;-)

也许如果你想使用 gnuplot 计算输入值的平均值(或其他函数),你可以使用我之前的答案的变体,它附加每个文件中的选定列但最后一个,使用:

# output a new table where column COL from each (but the last) input file is
# appended to the last input file
BEGIN {
    FILENUM = ARGC - 1
}
{
    if (ARGIND <= 1) {  # first file or stdin
        ADD[NR] = ""
    }
    if (ARGIND == FILENUM) {    # last file
        if (FILENUM > 1 && NF >= COL && $COL ~ /^-?[0-9]/)
            [=10=] = [=10=] ADD[FNR];
        print [=10=];
    } else {
        if (NF >= COL && $COL ~ /^-?[0-9]/) {
            ADD[FNR] = ADD[FNR] FS $COL;
        }
    }
}

将其应用于输入样本,您将得到: 输入 1:

720 0.403176
730 0.399838
# Lab = 73.45771 -0.552744 -2.636218

输入2:

720 0.394166
730 0.391083
# Lab = 72.911591 -0.718176 -2.942526

输入3:

720 0.364636
730 0.361698
# Lab = 70.623329 -0.713199 -2.19574

来自awk -f multi-column-add.awk -v COL=2 input{1,2,3}的输出:

720 0.364636 0.403176 0.394166
730 0.361698 0.399838 0.391083
# Lab = 70.623329 -0.713199 -2.19574

所以你可以绘制 using ((++)/3)。如果您喜欢最大值、最小值、误差线,请继续。