У меня есть сотни файлов в каталоге с именами File1
, File2
, File3
, ..., File200
. Предположим, у меня сейчас только 3 файла, содержимое этих файлов:
File1
: (столбцы разделены табуляцией)
[CITY] [SCORE]
City1 110
City2 120
City3 130
City4 140
File2
: (столбцы разделены табуляцией)
[CITY] [SCORE]
City1 210
City3 230
City4 240
File3
: (столбцы разделены табуляцией)
[CITY] [SCORE]
City1 310
City5 350
Здесь столбец [CITY]
— идентификатор, [SCORE]
— значение. Я хочу объединить эти файлы в одну матрицу на основе столбца идентификатора [CITY]
, заполнив NA
недостающие ячейки,
MergedMatrixFile
: (столбцы разделены табуляцией)
[CITY] File1 File2 File3
City1 110 210 310
City2 120 NA NA
City3 130 230 NA
City4 140 240 NA
City5 NA NA 350
Как я могу сделать это с Bash
.
Это хороший кандидат на GNU awk. Он имеет многомерные массивы. Вы можете использовать это, чтобы построить матрицу и распечатать ее в блоке END. Обратитесь к awk и нажмите ссылку «Подробнее».
Взгляните на join
.
Вот довольно уродливый скрипт, который далеко не эффективен и, возможно, содержит ошибки:
#!/bin/bash
TMP=$(mktemp -d)
# get the union of all city names
tail -q -n +2 File* | awk '{print $1}' | sort -u > ${TMP}/cities.txt
# fill in the missing entries for each file
for file in File*; do
tail -n +2 ${file} | cat - <(diff <(awk '{print $1}' ${file} | sort) ${TMP}/cities.txt | grep '^>') | sed 's/^> \(.*\)/\1 NA/' | sort | awk '{print $2}' > ${TMP}/${file}
done
# put it all together
paste ${TMP}/cities.txt ${TMP}/File* > merged.txt
Спасибо. Тестирую код, основная проблема - потеря имени файла в объединенном файле матрицы.
С awk и возможностью дополнительных файлов
awk 'FNR > 1 {
map[$1][FILENAME]=$2; # Create a two dimensional array with the city as the first index and the filename the second.
fils[FILENAME] = "" # Create an array of the file names
}
END {
PROCINFO["sorted_in"] = "@ind_str_asc"
printf "%s\t","[CITY]"; # Print the city header
for (c in fils) {
printf "%s\t",C# Loop through each file name in fils and print as a header
};
printf "\n";
for (i in map) {
printf "%s",i; # Print the city columns
for (j in fils ) {
!map[i][j]?str[j] = "NA":str[j]=map[i][j]; # Loop through the two dimensional array and if there is no entry for a given city/file name, set array str to NA (index file name) otherwise set to value in array
printf "\t%s",str[j] # Print the str variable
}
printf "\n"
}
}' file1 file2 file3
Один лайнер:
awk 'FNR > 1 { map[$1][FILENAME]=$2;fils[FILENAME] = "" } END { PROCINFO["sorted_in"] = "@ind_str_asc";printf "%s\t","[CITY]";for (c in fils) { printf "%s\t",c };printf "\n";for (i in map) { printf "%s",i;for (j in fils ) { !map[i][j]?str[j] = "NA":str[j]=map[i][j];printf "\t%s",str[j] } printf "\n" } }' file1 file2 file3
На самом деле в этом каталоге у меня сотни файлов, а не только 3.
Хорошо, я изменил решение.
Спасибо! Небольшая проблема: нам не нужна вкладка \t
за последней колонкой
ОК, я изменил расположение табуляции перед строками, а не после них.
Еще остались баги. Первая строка в выводе — это [CITY] File2 File3 File1
, а не [CITY] File1 File2 File3
. причин не узнаю.
Я добавил PROCINFO["sorted_in"] = "@ind_str_asc" в код, чтобы установить порядок массива. Это будет работать только с версиями GNU awk.
Привет, кажется, что этот скрипт все еще имеет некоторые ошибки. Когда дело доходит до больших данных, результат неверен. Например, если у меня есть 100 файлов, результаты этого скрипта и другого кода Python будут одинаковыми. Но когда дело доходит до 1000 файлов, результат этого скрипта неверен. Кажется, самый большой объем слитой матрицы из этого скрипта у меня на сервере ~1G. Мне интересно, связана ли проблема с ограничением количества массива в awk.
Мы призываем вопрошающих показать, что они пытались сделать до сих пор, чтобы решить проблему самостоятельно.