Как вычислить средний из конкретного месяца столбца, мудрого с годами?

У меня есть текстовый файл, имеющий температурные данные месяцев в апреле и мае в течение шести лет. Я хочу вычислить средний из каждого месяца с каждым годом. Я использую команду awk, но она вычисляет общую среднюю температуру. Я не знаю, как использовать команду awk для этой проблемы.

awk '{sum+=$6; n++} END {print sum/n;}' vk4.txt

Файл примера я показываю,

STATION_ID,LATITUDE,LONGITUDE,TIME(GMT),DATE(GMT),AIR_TEMP(°C)
IMDE1611_14164B(PITAMPURA)  28.7    77.15   1   04/05/2012  31.4
IMDE1611_14164B(PITAMPURA)  28.7    77.15   2   04/05/2012  31.9
IMDE1611_14164B(PITAMPURA)  28.7    77.15   3   04/05/2012  32.6
IMDE1611_14164B(PITAMPURA)  28.7    77.15   2   05/01/2012  32.1
IMDE1611_14164B(PITAMPURA)  28.7    77.15   3   05/01/2012  32.3
IMDE1611_14164B(PITAMPURA)  28.7    77.15   4   05/01/2012  33
IMDE1611_14164B(PITAMPURA)  28.7    77.15   5   04/01/2013  33.9
IMDE1611_14164B(PITAMPURA)  28.7    77.15   6   04/01/2013  34.2
IMDE1611_14164B(PITAMPURA)  28.7    77.15   7   04/01/2013  34.8
1
задан 3 June 2017 в 14:21

5 ответов

Вы могли сделать это с небольшим сценарием Python:

#!/usr/bin/env python3

import sys
if len(sys.argv) != 2:
    print("You must provide exactly one filename to read as argument.")
    exit(-1)

file = open(sys.argv[1])
file.readline()  # to strip headline

dict = {}
for line in file:
    datestr, tempstr = line.split()[4:]
    year, temp = int(datestr.split("/")[-1]), float(tempstr)
    dict.setdefault(year, []).append(temp)

for year in dict:
    print("{0}:\t{1:.2f}".format(year, sum(dict[year]) / len(dict[year])))

Это читает файл, указанный как аргумент при выполнении сценария линию за линией, и создает словарь, который отображает годы на списки температурных значений. После того, как целый файл был обработан, он вычислит и распечатает средние температуры в год.

Вот пример, выполненный с файлом данных vk4.txt Вы обеспечили. Я сохранил сценарий выше как avgtemp.py в текущем каталоге и сделанный этим исполняемое использование chmod +x avgtemp.py:

$ ./avgtemp.py vk4.txt
2012:   32.22
2013:   34.30

Если Вы хотите, точный выходной формат мог бы быть легко изменен путем простого редактирования "{0}:\t{1:.2f}" строка формата в последней строке сценария. Можно ввести любой шаблон здесь, пока он содержит a {0} быть замененным годом и {1:.2f} или подобный, чтобы быть замененным средней температурой, отображенной с двумя десятичными цифрами. \t вкладка.

1
ответ дан 3 December 2019 в 06:53

Основная идея будет состоять в том, чтобы создать ключ месяца года из поля даты, и затем суммировать и считать записи на основе того ключевого использования ассоциативными массивами, например.

awk '
  NR>1 {
    split($5,d,"/"); s[d[3]"/"d[1]]+=$6; c[d[3]"/"d[1]]++;
  } 
  END {
    for (i in s) print i, s[i]/c[i]
  }' vk4.txt

Тестирование с данными:

$ mawk '
  NR>1 {
    split($5,d,"/"); s[d[3]"/"d[1]]+=$6; c[d[3]"/"d[1]]++;
  } 
  END {
    for (i in s) print i, s[i]/c[i];
  }' vk4.txt
2012/04 31.9667
2012/05 32.4667
2013/04 34.3

Если у Вас есть GNU awk (gawk) v4 + можно добавить явную сортировку.

1
ответ дан 3 December 2019 в 06:53

Решение для Perl

Вот короткая команда, которая воздействует на предпосылку создания двух хешей - $h1 для подведения итогов температурных значений и $h2 для хранения общего количества обработанных записей. Каждое соответствие имеет, будет содержать тот же ключ в формате MMYYYY это извлечено из Вашего столбца № 5 (который является для perl индекс массива № 4, т.е. $F[4] ):

perl -lane 'do{ @a=split "/",$F[4]; $k= $a[0] . $a[2]; $h1{$k}+=$F[5] and $h2{$k}+=1 } if $. != 1 and $F[4]; END{ do {print $_," ",$h1{$_}/$h2{$_}  } for keys %h1;  }'

Ключевой пункт для замечания здесь:

  • мы используем do {} if condition1 and condition2 структура. {} действие выполняется только, когда номер строки не 1 (т.е. мы пропускаем заголовок) и существует $F[4] (т.е. мы избегаем пустых или неполных строк).

  • @a=split "/",$F[4] позволяет нам ломаться MM/DD/YYYY отметка даты в части и с $k= $a[0] . $a[2] мы создаем ключевую переменную, которая позволит нам хранить данные в два хеша.

  • END{} структура выполнит действие, когда целый файл будет считан.


Решение работает обоснованно хорошо. Вот тест с 1 100 000 строк входа:

bash-4.3$ time perl -lane 'do{ @a=split "/",$F[4]; $k= $a[0] . $a[2]; $h1{$k}+=$F[5] and $h2{$k}+=1 } if $. != 1 and $F[4]; END{ do {print $_," ",$h1{$_}/$h2{$_}  } for keys %h1;  }' big_input.txt
052012 32.4666666666021
042012 31.8250000001141
042013 34.3000000000646

real    0m8.600s
user    0m8.480s
sys 0m0.032s
bash-4.3$ wc -l big_input.txt 
1100000 big_input.txt

Примечание: для использования формата CSV perl -a -F',' -lne вместо этого

0
ответ дан 3 December 2019 в 06:53

Это могло бы больше подходить для Переполнения стека; однако, вот решение с помощью Python, в котором необходимо заменить temperature_data.txt в первой строке с Вашим файлом.

f=open("temperature_data.txt","r") ### REPLACE temperature_data.txt WITH THE FILE CONTAINING YOUR DATA
flines=f.readlines() #read the file in question
f.close()

flines_split=[line.split() for line in flines] #split each line up
data_split=[line for line in flines_split if len(line)>=5 and line[4].count("/")==2] #get only lines with the date in
gathered_data={}
for line in data_split: #this block sanitises the data
    month=int(line[4][:2]) ### NOTE THAT THIS ASSUMES YOU ARE USING AMERICAN DATE FORMAT
    ### IF YOU ARE NOT, REPLACE "month=int(line[4][:2])" WITH "month=int(line[4][3:5])"
    year=int(line[4][6:])
    if (month,year) in gathered_data:
        gathered_data[(month,year)].append(float(line[5]))
    else:
        gathered_data[(month,year)]=[float(line[5])]

def mean(l): #function to calculate means
    return sum(l)/float(len(l))

means={k:mean(gathered_data[k]) for k in gathered_data} #calculate means

print("Month Year Temperature")
for k in sorted(list(means)): #print output
    print("{date[0]:^5} {date[1]} {temp:.4}".format(date=k,temp=means[k])) ### the 4 in {temp:.4} specifies precision and can be modified.
0
ответ дан 3 December 2019 в 06:53

Другой – очень гибкий – решение Python на основе itertools.groupby: https://github.com/davidfoerster/group-aggregate

Установка

wget https://github.com/davidfoerster/group-aggregate/raw/master/group-aggregate.py
chmod +x group-aggregate.py

Использование

./group-aggregate.py [--skip N] [options...] groups aggregators...
  • groups – Список полевых индексов или диапазонов столбца раньше группировал записи (основанный на нуле, разделенный от запятой).

  • aggregators – Полевой (основанный на нуле) индекс или диапазон столбца, название функции агрегирования и дополнительно строка формата, все разделенные от двоеточия.

  • --skip N - Пропустите строки N в начале входа (например, строки заголовка).

Посмотрите вывод python3 -O group-aggregate.py --help для больше.

Примеры

Пример 1

Программа группировки и агрегирования не может обработать частичные поля; давайте переформатируем Ваш набор данных с другими инструментами для работы вокруг этого:

awk '{ gsub(/\//, OFS, $5); print; }'  | ...

Теперь группирующееся поле, год, имеет индекс 6, и агрегированное поле, температуры, имеет индекс 7, которого требуется взять среднее число:

... | ./group-aggregate.py --skip 1 6 7:favg < data.csv

Можно также отформатировать температурные средние числа в этом примере для показа точно одного десятичного разряда:

... | ./group-aggregate.py --skip 1 6 7:favg:.1f

Пример 2

Вместо разделителей полей можно также указать диапазоны столбца, который работает хорошо с форматом данных:

./group-aggregate.py --skip 1 54-58 60-:favg:.1f < data.csv

Теперь Вы не должны даже предварительно форматировать данные как в примере 1.

Вывод

Вывод обеих команд в качестве примера является тем же:

2012    32.2
2013    34.3
2
ответ дан 3 December 2019 в 06:53

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

Похожие вопросы: