суммируйте по году и вставьте недостающие записи с 0 - PullRequest
2 голосов
/ 22 марта 2019

У меня есть отчет для записей год-месяц, как показано ниже

201703 5
201708 10
201709 20
201710 40
201711 80
201712 100
201802 0
201803 25
201804 50
201805 50
201806 150
201807 300
201808 200
201902 10 

Мне нужно суммировать записи года-месяца по годам и печатать после всех месяцев для этого конкретного года.Год-месяц может содержать пропущенные записи для любого месяца.Для этих месяцев необходимо вставить фиктивное значение (0).

Требуемый вывод:

201703 5
201704 0
201705 0
201706 0
201707 0
201708 10
201709 20
201710 40
201711 80
201712 100
2017 255
201801 0
201802 0
201803 25
201804 50
201805 50
201806 150
201807 300
201808 200
201809 0
201810 0
201811 0
201812 0
2018 775
201901 0
201902 10
201903 0
2019 10

Я могу получить сводку года, используя следующую команду.

awk ' { c=substr($1,0,4); if(c!=p) { print p,s ;s=0} s=s+$2 ; p=c ; print } ' ym.dat

Но как вставить записи для недостающих?Также последняя запись не должна превышать текущий (системное время) год-месяц.то есть для этого конкретного примера, фиктивные значения не должны быть вставлены для 201904..201905 .. и т. д. Он должен просто остановиться на 201903

Ответы [ 5 ]

2 голосов
/ 22 марта 2019

С GNU awk для strftime ():

$ cat tst.awk
NR==1 {
    begDate = $1
    endDate = strftime("%Y%m")
}
{
    val[$1] = $NF
    year = substr($1,1,4)
}
year != prevYear { prt(); prevYear=year }
END { prt() }

function prt(   mth, sum, date) {
    if (prevYear != "") {
        for (mth=1; mth<=12; mth++) {
            date = sprintf("%04d%02d", prevYear, mth)
            if ( (date >= begDate) && (date <=endDate) ) {
                print date, val[date]+0
                sum += val[date]
                delete val[date]
            }
        }
        print prevYear, sum+0
    }
}

.

$ awk -f  tst.awk file
201703 5
201704 0
201705 0
201706 0
201707 0
201708 10
201709 20
201710 40
201711 80
201712 100
2017 255
201801 0
201802 0
201803 25
201804 50
201805 50
201806 150
201807 300
201808 200
201809 0
201810 0
201811 0
201812 0
2018 775
201901 0
201902 10
201903 0
2019 10

С другими awk вы просто передаете endDate, используя awk -v endDate=$(date +'%Y%m') '...'

2 голосов
/ 22 марта 2019

Вы можете использовать этот awk скрипт mmyy.awk:

{
   rec[$1] = $2;
   yy=substr($1, 1, 4)
   mm=substr($1, 5, 2) + 0
   ys[yy] += $2
}

NR == 1 {
   fm = mm
   fy = yy
}

END {
   for (y=fy; y<=cy; y++)
      for (m=1; m<=12; m++) {
         # print previous years sums
         if (m == 1 && y-1 in ys)
            print y-1, ys[y-1]

         if (y == fy && m < fm)
            continue;
         else if (y == cy && m > cm)
            break;

         # print year month with values or 0 if entry is missing
         k = sprintf("%d%02d", y, m)
         printf "%d%02d %d\n", y, m, (k in rec ? rec[k] : 0)
      }
      print y-1, ys[y-1]
}

Затем назовите его так:

awk -v cy=$(date '+%Y') -v cm=$(date '+%m') -f mmyy.awk file

201703 5
201704 0
201705 0
201706 0
201707 0
201708 10
201709 20
201710 40
201711 80
201712 100
2017 255
201801 0
201802 0
201803 25
201804 50
201805 50
201806 150
201807 300
201808 200
201809 0
201810 0
201811 0
201812 0
2018 775
201901 0
201902 10
201903 0
2019 10
1 голос
/ 22 марта 2019

Хороший вопрос, кстати.Пятница после полудня.Время отправляться домой.

В awk.Необязательное время окончания и его значение вводятся в качестве аргументов:

$ awk -v arg1=201904 -v arg2=100 '          # optional parameters
function foo(ym,v) {
    while(p<ym){
        y=substr(p,1,4)                     # get year from previous round
        m=substr(p,5,2)+0                   # get month
        p=y+(m==12) sprintf("%02d",m%12+1)  # December magic
        if(m==12)
            print y,s[y]                    # print the sums (delete maybe?)
        print p, (p==ym?v:0)                # print yyyymm and 0/$2
    }
}
{
    s[substr($1,1,4)]+=$2                   # sums in array, year index
}
NR==1 {                                     # handle first record
    print
    p=$1
}
NR>1 {
    foo($1,$2)
}
END {
    if(arg1)
        foo(arg1,arg2)
    print y=substr($1,1,4),s[y]+arg2
}' file

Хвост с выхода:

2018 775
201901 0
201902 10
201903 0
201904 100
2019 110
1 голос
/ 22 марта 2019

Следующий скрипт awk сделает то, что вы ожидаете.Идея такова:

  • хранить данные в массиве
  • печатать и суммировать только при изменении года

Это дает:

# function that prints the year starting
# at month m1 and ending at m2
function print_year(m1,m2,   s,str) {
    s=0
    for(i=(m1+0); i<=(m2+0); ++i) { 
       str=y sprintf("%0.2d",i);
       print str, a[str]+0; s+=a[str]
    }
    print y,s
}

# This works for GNU awk, replace for posix with a call as
# awk -v stime=$(date "+%Y%m") -f script.awk file
BEGIN{ stime=strftime("%Y%m") }
# initializer on first record    
(NR==1){ y=substr($1,1,4); m1=substr($1,5) }
# print intermediate year
(substr($1,1,4) != y) { 
    print_year(m1,12)
    y=substr($1,1,4); m1="01";
    delete a
}
# set array value and keep track of last month
{a[$1]=$2; m2=substr($1,5)}
# check if entry is still valid (past stime or not)
($1 > stime) { exit }
# print all missing years full
# print last year upto system time month
END { 
  for (;y<substr(stime,1,4)+0;y++) { print_year(m1,12); m1=1; m2=12; }
  print_year(m1,substr(stime,5))
}
1 голос
/ 22 марта 2019

Perl для спасения!

perl -lane '$start ||= $F[0];
            $Y{substr $F[0], 0, 4} += $F[1];
            $YM{$F[0]} = $F[1];
            END { for $y (sort keys %Y) {
                      for $m (1 .. 12) {
                          $m = sprintf "%02d", $m;
                          next if "$y$m" lt $start;
                          print "$y$m ", $YM{$y . $m} || 0;
                          last if $y == 1900 + (localtime)[5]
                               && (localtime)[4] < $m;
                      }
                      print "$y ", $Y{$y} || 0;
                  }
              }' -- file
  • -n читает строку ввода строкой
  • -l удаляет символы новой строки из ввода и добавляет их в вывод
  • -a разбивает каждую строку на пустом месте в массив @F

  • substr извлекает год из даты ГГГГММ.Хэши% Y и% YM используют даты и ключи, а также значения в качестве значений.Вот почему годовой хэш использует +=, который добавляет значение к уже накопленному.

  • Блок END оценивается после исчерпания ввода.
  • Он просто повторяетсягоды, сохраненные в хэше, диапазон 1 .. 12 используется для месяца, чтобы вставить нули (оператор || печатает его).
  • next и $start пропускаетмесяцев до начала отчета.
  • last отвечает за пропуск остальной части текущего года.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...