闰年 - 推断值

闰年 - 推断值

我有一些表(table.txt)如下:

YEAR MONTH DAY RES
1971 1     1   1345
1971 1     2   1265
1971 1     3   1167

每个时间序列的长度从1.1.1971直到31.12.2099。不幸的是,某些时间序列缺少闰年及其值(例如,1972 年是闰年,因此 2 月应该有 29 天,但我的时间序列在 1972 年 2 月只有 28 天)。例如,在我当前的表格中,1972 年 2 月底的情况如下:

YEAR MONTH DAY RES
1972 2     27  100
1972 2     28  101
1972 3     1   102

这是错误的,因为它没有考虑任何闰年。相反,我想通过推断前一天和后一天的值,将时间序列中每个闰年的每个缺失天(显然是 2 月 29 日)包含在我的时间序列中,如下所示:

YEAR MONTH DAY RES
1972 2     27  100
1972 2     28  101
1972 2     29  101.5
1972 3     1   102

有没有办法使用 shell/bash 来做到这一点?

答案1

也许是这样的:

awk '
  function isleap(y) {
    return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)
  }
  $2 == 3 && $3 == 1 && isleap($1) && last_day != 29 {
    print $1, 2, 29, (last_data + $4) / 2
  }
  {print; last_day = $3; last_data = $4}' file

答案2

我只是在想这个问题,由于闰年每隔偶数年交替一次,以下情况是正确的:

([13579][26]|[02468][048]) == leap year

基本上,奇数十年的闰年发生在第 2 年和第 6 年,偶数十年的闰年发生在第 4 年和第 8 年,以及每隔一个十年的交替年。

所以你可以这样做:

sed -e'  /[02468] * 2 * 28 /!b'\
    -e'h;/[13579][26] * 2 / G' \
    -e'  /[02468][048] * 2 /G' \
    -e'  /\n/s/ 28 / 29 /2'    \
    -eP\;D <in >out

...它将查找、加倍然后修改输入中的所有 2 月 28 日行,仅适用于闰年,无论任何交替循环的起点如何。


这是我的第一直觉:

sed -e'/\([02648] * 2 * 2\)8 /!b' \
    -e:n -e'n;//!bn' -e'p;s//\19 /' <in

...这只是对我对你的回答的轻微调整其他问题,但它只适用于遇到的第一个偶数年不是闰年的每个系列,因为它是交替进行的。

sed我根据你的测试文件测试了这两个其他问题。当然,infile 已经有闰年了,我用来生成它的代码也在答案中,但是两者都适用于从 1970 年开始的系列,尽管第一个无论如何都不会中断:


1970  2   27  58
1970  2   28  59
1970  3   1   60
1972  2   27  58
1972  2   28  59
1972  2   29  59
1972  2   29  60
1972  3   1   61
1974  2   27  58
1974  2   28  59
1974  3   1   60
1976  2   27  58
1976  2   28  59
1976  2   29  59
1976  2   29  60
1976  3   1   61
1978  2   27  58
1978  2   28  59
1978  3   1   60
1980  2   27  58
1980  2   28  59
1980  2   29  59
1980  2   29  60
1980  3   1   61

答案3

Perl解决方案:

#!/usr/bin/perl
use warnings;
use strict;

use Time::Piece;

print scalar <>; # Skip the header.

while (<>) {
    my ($year, $month, $day, $res) = split;
    my $t = 'Time::Piece'->strptime("$year $month $day", '%Y %m %d');
    if ($t->is_leap_year && 2 == $month && 28 == $day) {
        print;
        $_ = <>;
        my ($year2, $month2, $day2, $res2) = split;
        die "Expected March the 1st: $_"
            unless $year == $year2 && 3 == $month2 && 1 == $day2;
        print join("\t", $year, 2, 29, ($res + $res2) / 2), "\n";
    }
    print;
}

另存为fix_feb29.pl。然后运行

for file in *.txt ; do
    fix_feb29.pl -i~ "$file"
done

相关内容