Рассчитать среднее значение между столбцами нескольких файлов в 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)
, Если вы чувствуете, что максимумы, минимумы, ошибки, просто продолжайте.