次のサンプルデータ項目を含む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
sort
uniq
質問に表示される出力の例を提供できます。
$ 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
orまたは何かを使用する必要があります。たとえば、以下は、カンマで区切られた最初の2つのフィールド(日付と時刻など)のみを比較します。perl
uniq
awk
$ sed -e 's/Timestamp/Date,Hour/; s/ \(..\):[^,]*,/,\1,/' file.csv |
awk -F, 'prev != $1$2 {print; prev=$1$2}'
sed
ただし、出力をパイプにしたい場合は、awkができるすべての操作を実行できるため、単独で使用することもできますawk
。これがawkと関数の目的です。例えばawk
sed
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