タイムスタンプ列に基づいて日付と時刻列をマージして追加する

タイムスタンプ列に基づいて日付と時刻列をマージして追加する

次のサンプルデータ項目を含むcsvファイルがあります。

Timestamp,data1,data2
2018 07 16 13:00:00,23,45
2018 07 16 13:10:00,23,45
2018 07 16 13:20:00,23,45
2018 07 16 13:30:00,23,45
2018 07 16 13:50:00,23,45
2018 07 16 14:20:00,23,45
2018 07 16 14:40:00,23,45
2018 07 16 14:50:00,23,45
2018 07 16 15:10:00,23,45
2018 07 16 17:50:00,23,45
2018 07 16 18:10:00,23,45
2018 07 17 10:10:00,23,45
2018 07 18 13:20:00,23,45
2018 07 19 13:30:00,23,45

私がしたいのは、Date&という2つの異なる列を作成することですHour。列Dateには日付が含まれ、列にはHourデータがキャプチャされたすべての時間が含まれます。たとえば、上記のデータに基づいて、次のような出力が必要です(同じファイルに2つの列のみを追加します)。

Date,Hour
2018 07 16,13
2018 07 16,14
2018 07 16,15
2018 07 16,17
2018 07 16,18
2018 07 17,10
2018 07 18,13
2018 07 19,13

たとえば、2018年7月16日に13時間(1つ以上)のエントリがある場合は、その日付と13時間を一度だけ一覧表示し、日付が変更されるまで別の時間でアイテムを処理し続けます。そしてその過程を繰り返してください。

上記のように、ファイルには1日にわたって多数のアイテム(100000以上)があり、1時間にキャプチャされるデータの量は異なります。この問題をどのように解決できますか?私の説明が十分に明確であることを願っています。

答え1

使用awk:

awk 'BEGIN{ OFS=FS="," }
  NR==1{ print "Date", "Hour"; next }
  {
    $0=substr($1, 1, 10) FS substr($1, 12, 2)
    if ($0 == prev) next  # skip to next record if record equals prev
    prev=$0               # remember record
  }
  1                       # print record
' file

したがって、日付文字列は最初のフィールドの位置1から始まる最初の10文字で構成され、時刻は位置12で始まる2つの文字から抽出されます。

以前に記憶されたレコードが異なる場合、2つの値とフィールド区切り文字(FS)がレコード()に割り当てられて印刷されます。$0

答え2

sortuniq質問に表示される出力の例を提供できます。

$ sed -e 's/Timestamp.*/Date,Hour/; s/ \(..\):.*/,\1/' file.csv  | uniq
Date,Hour
2018 07 16,13
2018 07 16,14
2018 07 16,15
2018 07 16,17
2018 07 16,18
2018 07 17,10
2018 07 18,13
2018 07 19,13

しかし、これら2つの新しいフィールドが現在の入力行に追加されることを望んでいると言いました。これは各行で繰り返される日時で終わるので、私にとってはあまり意味がありません(すでにタイムスタンプフィールドの各行の先頭にあります)。

以下はまさにあなたが要求したものではありませんが、私の考えでは改善されたものです。

各行の末尾に日付と時刻を追加する代わりに、既存のタイムsedスタンプフィールドを日付と時刻のフィールドに変換します。その後、uniq重複行を削除するために使用されます。

$ sed -e 's/Timestamp/Date,Hour/; s/ \(..\):[^,]*,/,\1,/' file.csv  | uniq
Date,Hour,data1,data2
2018 07 16,13,23,45
2018 07 16,14,23,45
2018 07 16,15,23,45
2018 07 16,17,23,45
2018 07 16,18,23,45
2018 07 17,10,23,45
2018 07 18,13,23,45
2018 07 19,13,23,45

これは、入力ファイルがすでにタイムスタンプ順になっていると仮定します。

注:data1または値data2が異なる場合は、出力行は一意ではなく行が印刷されます。これはuniq、行全体が前の行と比較されるためです(uniqフィールドはスキップできますが、空白のみのフィールド区切り文字として認識され、カンマまたは最初の2つのフィールドのみが認識されません)。それがあなたが望むものなら、そのまま動作します。

それ以外の場合は、一意性を確認する代わりにawkorまたは何かを使用する必要があります。たとえば、以下は、カンマで区切られた最初の2つのフィールド(日付と時刻など)のみを比較します。perluniqawk

$ sed -e 's/Timestamp/Date,Hour/; s/ \(..\):[^,]*,/,\1,/' file.csv  |
    awk -F, 'prev != $1$2 {print; prev=$1$2}'

sedただし、出力をパイプにしたい場合は、awkができるすべての操作を実行できるため、単独で使用することもできますawk。これがawkと関数の目的です。例えばawksedsub()gsub()gensub()

$ awk -F, -v OFS=, '{ sub(/Timestamp/,"Date,Hour");
                       $1 = gensub(/ ([0-9]+):.*/,",\\1",1,$1)
                    };
                    prev != $1$2 {print; prev=$1$2}' file.csv

または次のようにperl

$ perl -lne 's/Timestamp/Date,Hour/;
             s/ (\d\d):.*?,/,$1,/;
             ($current) = (m/^[^,]+,\d\d|^Date),/);
             if ($prev ne $current) {print ; $prev = $current}' file.csv

関連情報