うるう年 - 推定値

うるう年 - 推定値

次のテーブル(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年に発生し、隔年で10年ごとに発生します。

だからあなたはこれを行うことができます:

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あなたのテストファイルに基づいてこれら2つをテストしました。その他の問題。もちろん、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

関連情報