У меня есть 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
Например, если есть записи для 13-го часа (независимо от того, одна или несколько) 16 июля 2018 г., укажите соответствующую дату и 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
Таким образом, строка даты состоит из первых 10 символов, начиная с позиции 1 первого поля, а час извлекается из 2 символов, начиная с позиции 12.
Оба значения плюс разделитель полей ( 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
Однако вы также сказали, что хотите, чтобы эти два новых поля были добавлены к текущим строкам ввода. Для меня это не имеет особого смысла, потому что тогда вы получите дублирование даты и часа в каждой строке (они уже находятся в начале каждой строки в поле Timestamp).
Нижеследующее — не совсем то, что вы просили, но, на мой взгляд, это улучшение.
Вместо добавления даты и часа в конец каждой строки, он просто 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
можно сделать так, чтобы пропускались поля, но распознает только пробелы как разделители полей и нельзя сделать так, чтобы использовались запятые, и нельзя сделать так, чтобы использовались только первые два поля). Если это то, что вам нужно, то это будет работать как есть.
В противном случае вам придется использовать awk
или 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 может делать все, что 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