我有一些表(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