次のようなサンプル データ エントリを含む 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 つの列とを作成することですDate
。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 文字から抽出されます。
両方の値とフィールド区切り記号 ( 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
それ以外の場合は、 の代わりにまたは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