タイムスタンプ列に基づいて日付と時間の列を統合して追加します

タイムスタンプ列に基づいて日付と時間の列を統合して追加します

次のようなサンプル データ エントリを含む 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

私がやりたいのは、他の 2 つの列とを作成することですDateHourDateには日付が含まれ、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 文字から抽出されます。

両方の値とフィールド区切り記号 ( 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 つのフィールドのみを使用するように設定したりすることはできません)。それが必要な場合は、そのまま動作します。

awkそれ以外の場合は、 の代わりにまたはperlなどを使用して一意性をチェックする必要がありますuniq。たとえば、次の例では、 のawk最初の 2 つのコンマ区切りフィールド (つまり、日付と時間) のみを比較します。

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

sedしかし、 の出力を にパイプする場合はawk、 を単独で使用してもかまいませんawk。awk は が実行できることはすべて実行できるからですsed。それが awk のsub()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

関連情報