Рассчитать среднее значение между столбцами нескольких файлов в gnuplot для 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

Я хотел бы извлечь данные из столбца 2 из всех zb_p.xy файлы, и выведите среднее значение между ними в сравнении со стандартным отклонением, используя gnuplot в linux.

Это была моя попытка:

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')

1 ответ

Решение

Вы можете вставить все файлы в один, используя следующую команду 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

Эти выходные данные могут быть переданы в gnuplot как временный файл (с 6 столбцами), так что вы можете манипулировать столбцами, которые нужно построить:

# gnuplot
data = "<( paste */file.dat )"
plot data u 1:(($2+$4+$6)/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 игнорирует строки, начинающиеся с символа #,

И, наконец, вы можете построить график зависимости std-dev от среднего в 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 $0;
    }
}

С входными файлами input1, input2, а также input3 Я использую команду

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

создавать output, Как очень простой тестовый прогон, рассмотрим эти примеры данных:

input1:

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

input2:

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).

Наконец пример графика с моими полными данными (B1, B2, а также B3 исходные входные файлы, и 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]/)
            $0 = $0 ADD[FNR];
        print $0;
    } else {
        if (NF >= COL && $COL ~ /^-?[0-9]/) {
            ADD[FNR] = ADD[FNR] FS $COL;
        }
    }
}

Применяя его к входным выборкам, вы получите: input1:

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

input2:

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 (($2+$3+$4)/3), Если вы чувствуете, что максимумы, минимумы, ошибки, просто продолжайте.

Другие вопросы по тегам