我有一个 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
& 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 年 07 月 16 日的 13 小时(无论是 1 个还是多个)有条目,则仅列出相应的日期和 13 小时一次,然后继续处理具有不同小时的条目,直到日期发生变化。并重复该过程。
请注意,该文件在多天内有许多条目(100000+),一小时内捕获的数据数量各不相同,如上所述。我该如何解决这个问题?我希望我的解释足够清楚。
答案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
但是,您还说过您希望将这两个新字段附加到当前输入行。这对我来说没有多大意义,因为这样您最终会在每行中重复出现日期和时间(它们已经位于时间戳字段中每行的开头)。
以下内容并不完全是您所要求的,但在我看来是一种改进。
它不是将日期和时间附加到每行的末尾,而是只是将sed
现有的时间戳字段转换为日期和时间字段。 thenuniq
用于消除重复行。
$ 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
可以跳过字段,但只能将空格识别为字段分隔符,不能使用逗号,也不能仅使用前两个字段) 。如果这就是您想要的,那么它就会按原样工作。
否则,您需要使用awk
or perl
or 某些东西而不是uniq
检查唯一性。例如,以下用于awk
仅比较前两个逗号分隔的字段(即日期和时间):
$ sed -e 's/Timestamp/Date,Hour/; s/ \(..\):[^,]*,/,\1,/' file.csv |
awk -F, 'prev != $1$2 {print; prev=$1$2}'
但是如果您要将sed
into的输出通过管道传输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