Вычисление средних значений кусков столбца - PullRequest
0 голосов
/ 19 июня 2019

У меня есть файл с разделителями табуляции

LOC105758527    1       55001   0.469590
LOC105758527    1       65001   0.067909
LOC105758527    1       75001   0.220712
LOC100218126    1       85001   0.174872
LOC105758529    1       125001  0.023420
NRF1    1       155001  0.242222
NRF1    1       165001  0.202569
NRF1    1       175001  0.327963
UBE2H   1       215001  0.063989
UBE2H   1       225001  0.542340
KLHDC10 1       255001  0.293471
KLHDC10 1       265001  0.231621
KLHDC10 1       275001  0.142917
TMEM209 1       295001  0.273941
CPA2    1       315001  0.181312

Мне нужно вычислить среднее значение для столбца 4 для каждого элемента в столбце 1. Таким образом, сумма / количество строк и выведите столбец 1, 2, 3 первой строки в вычислении, а среднее значение - как столбец 4.

Я начал с того, что сделал сумму

awk 'BEGIN { FS = OFS = "\t" }
        { y[$1] += $4; $4 = y[$1]; x[$1] = $0; }
END { for (i in x) { print x[i]; } }' file

Но я получаю

NRF1    1       175001  0.772754
LOC105758529    1       125001  0.02342
LOC100218126    1       85001   0.174872
KLHDC10 1       275001  0.668009
CPA2    1       315001  0.181312
TMEM209 1       295001  0.273941
UBE2H   1       225001  0.606329
LOC105758527    1       75001   0.758211

Это означает, что он переходит на какую-то строку, отличную от 1-й, в моем файле (и печатает col1,2,3 из последней вычисленной строки - это хорошо, но я бы предпочел 1-ю строку вместо этого). Выход из строя.

Я также не знаю, как разделить сумму на их NR, чтобы получить среднее значение

Ответы [ 3 ]

1 голос
/ 20 июня 2019

Это можно сделать всего лишь через awk, используя массивы для хранения порядка строк и промежуточных шагов вычисления:

# set fields delimiters
BEGIN { FS = OFS = "\t" }

# print the header
NR==1 { print; next }

# the first time col1 value occurs, store col1..col3
!h[$1] {
    h[$1] = ++n  # save ordering
    d[n] = $1 OFS $2 OFS $3  # save first 3 columns
}

# store sum and quantity of col4
{
    i = h[$1]  # recover ordering
    s[i] += $4
    q[i]++
}

# output col1..col3 and the average value
END {
    for (i=1; i<=n; i++) print d[i], s[i]/q[i]
}

Я вижу, вы редактировали вопрос с тех пор, как я написал выше. Если ваши данные не имеют заголовка, строка NR==1 не потребуется.

Если ваш файл данных действительно большой, приведенный выше скрипт может потреблять слишком много памяти (он будет использовать память, пропорциональную количеству уникальных значений для col1). Если это будет проблематично, а порядок строк вывода не важен, использование памяти может быть резко сокращено путем предварительной сортировки данных (возможно, с sort -k1,1 -s) и создания выходных данных постепенно:

BEGIN { FS = OFS = "\t" }

$1 != c1 {
    if (c1) print d, s/q
    d = $1 OFS $2 OFS $3
    s = q = 0
    c1 = $1
}

{
    s += $4
    q++
}

END { print d, s/q }
1 голос
/ 20 июня 2019

Вот попытка awk сценария.Не уверен, что я полностью понимаю задачу с таким количеством изменений.

script.awk

BEGIN {OFS="\t"}  # assign output field separtor to tab
$1 == currFields[1]{  # handle a repeated line
    lineCount++;      # increment line counter
    currFields[4] += $4; # accumulate the sum of 4th field
    next;             # skip any output and read next line.
}
{                     # handle a new line not seen before
    outputPrevLine(); # print out the prvious line
    for(i = 1; i <= NF; i++) currFields[i] = $i; # store current line values
    lineCount = 0;    # reset line counter
}
END {outputPrevLine();} # output the last line even if repeated.
function outputPrevLine() { 
    if (NR == 1) return; # discard first line, since it contains no value.
    print currFields[1], currFields[2], currFields[3], currFields[4]/(lineCount + 1);
}

input.txt

LOC105758527    1       55001   0.469590
LOC105758527    1       65001   0.067909
LOC105758527    1       75001   0.220712
LOC100218126    1       85001   0.174872
LOC105758529    1       125001  0.023420
NRF1    1       155001  0.242222
NRF1    1       165001  0.202569
NRF1    1       175001  0.327963
UBE2H   1       215001  0.063989
UBE2H   1       225001  0.542340
KLHDC10 1       255001  0.293471
KLHDC10 1       265001  0.231621
KLHDC10 1       275001  0.142917
TMEM209 1       295001  0.273941
CPA2    1       315001  0.181312

работает:

 sort input.txt | awk -f script.awk 

вывод:

CPA2    1       315001  0.181312
KLHDC10 1       255001  0.22267
LOC100218126    1       85001   0.174872
LOC105758527    1       55001   0.252737
LOC105758529    1       125001  0.02342
NRF1    1       155001  0.257585
TMEM209 1       295001  0.273941
UBE2H   1       215001  0.303165
0 голосов
/ 20 июня 2019

Так как я не мог понять, как индексировать массив и как разделить сумму сразу на число, я решил решить эту проблему, отсортировав вывод

awk 'BEGIN { FS = OFS = "\t" }
        { y[$1] += $4; $4 = y[$1]; x[$1] = $0; }
END { for (i in x) { print x[i]; } }' file | sort -V -k2,3 > temp1

и сохранив его в файле temp1.

Затем посчитайте уникальные вхождения в col1 в моем исходном файле вместе с некоторым переформатированием текста, выполнив

cut -f1 test | uniq -c | sed 's/^\s\+//g ; s/\s/\t/' > temp2

и сохранив в файле temp2

, затем я просто вставил temp1и temp2 и разделил столбец суммы на столбец подсчета, выполнив

paste temp1 temp2 | awk -F$'\t' '{$6 = $4 / $5}1' > output

Где последний 4-й столбец - сумма, 5-й столбец - это число, а 6-й столбец - среднее значение

LOC105758527  1  75001   0.758211  3  0.252737
LOC100218126  1  85001   0.174872  1  0.174872
LOC105758529  1  125001  0.02342   1  0.02342
NRF1          1  175001  0.772754  3  0.257585
UBE2H         1  225001  0.606329  2  0.303165
KLHDC10       1  275001  0.668009  3  0.22267
TMEM209       1  295001  0.273941  1  0.273941
CPA2          1  315001  0.181312  1  0.181312

Тогда rm временные файлы

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...