Резюме:
В настоящее время у меня есть два файла .txt, импортированные из системы опросов, которую я тестирую. Столбец 1 каждого файла данных представляет собой отметку времени в формате «ЧЧММСС.СССССС». В файле 1 есть второй столбец с показаниями напряженности поля. В файле2 есть два дополнительных столбца позиционной информации. Я пытаюсь написать сценарий, который сопоставляет точки данных между этими файлами, выстраивая временные метки. Проблема в том, что ни одна из временных меток не является одним и тем же значением. Сценарий должен иметь возможность сопоставлять точки данных (строки в каждом файле .txt) на основе временной метки своего ближайшего аналога в другом файле (т. е. время 125051.354948 из файла 1 должно «совпадать» с ближайшей временной меткой в файле 2, то есть 125051.112784).
Если бы кто-нибудь с немного большим знанием awk/sed/join/regex/Unix мог указать мне правильное направление, я был бы очень признателен.
Что у меня есть до сих пор:
(Обратите внимание, что точный синтаксис, показанный здесь, может не иметь смысла для примеров файлов .txt, прикрепленных к этому вопросу, существуют более расширенные версии этих файлов с большим количеством столбцов, которые использовались для тестирования скриптов.)
Я новичок в сценариях awk/Unix/shell, поэтому, пожалуйста, потерпите меня, если некоторые из этих пробных решений не работают или не имеют большого смысла.
Я уже пробовал некоторые решения, опубликованные здесь, о переполнении стека с использованием соединения, но, похоже, он не хочет правильно сортировать или объединять любой из этих файлов:
${
join -o 1.1,2.2 -1 2 -2 1 <(sort -k 2 file1) <(sort -k 1 file2)
join -v 1 -o 1.1,1.2 -1 2 -2 1 <(sort -k 2 file1) <(sort -k 1
file2)
} | sort -k 1
Я попытался перенастроить существующие решения awk, которые также были опубликованы здесь:
awk 'BEGIN {FS=OFS = "\t"} NR==FNR {v[$3]=$2; next} {print $1, (v[$3] ?
v[$3] : 0)}' file1 file2 > file3
awk 'BEGIN {FS=OFS = "\t"} NR==FNR {v[$1]=$2; next} {print $1, (v[$1] ?
v[$1] : 0)}' file1 file2 > file3
Результат: обе эти awk-команды приводят к выводу файлов file2. данные, в которые ничего из файла file1 не включено (или так кажется).
awk -F '
FNR == NR {
time[$3]
next
}
{ for(i in time)
if (index($3, i) == 1) {
print
next
}
}' file1 file2 > file3
Результат: продолжает возвращать синтаксическую ошибку относительно "." из ".txt"
Я пытался интегрировать в сценарий какое-то регулярное выражение или команду разделения... но не знал, как действовать дальше, и не нашел ничего существенного.
Образец данных
$ cat file1.txt
125051.354948 058712.429
125052.352475 058959.934
125054.354322 058842.619
125055.352671 058772.045
125057.351794 058707.281
125058.352678 058758.959
$ cat file2.txt
125050.105886 4413.34358 07629.87620
125051.112784 4413.34369 07629.87606
125052.100811 4413.34371 07629.87605
125053.097826 4413.34373 07629.87603
125054.107361 4413.34373 07629.87605
125055.107038 4413.34375 07629.87604
125056.093783 4413.34377 07629.87602
125057.097928 4413.34378 07629.87603
125058.098475 4413.34378 07629.87606
125059.095787 4413.34376 07629.87602
Ожидаемый результат:
(Формат: Столбец1Файл1 Столбец1Файл2 Столбец2Файл1 Столбец2Файл2 Столбец3Файл2)
$ cat file3.txt
125051.354948 125051.112784 058712.429 4413.34358 07629.87620
125052.352475 125052.100811 058959.934 4413.34371 07629.87605
125054.354322 125054.107361 058842.619 4413.34373 07629.87605
125055.352671 125055.107038 058772.045 4413.34375 07629.87604
125057.351794 125057.097928 058707.281 4413.34378 07629.87603
125058.352678 125058.098475 058758.959 4413.34378 07629.87606
Как показано, не каждая точка данных из каждого файла найдет соответствие. В новый файл будут записаны только те пары строк, которые имеют наиболее близкие временные метки друг к другу.
Как упоминалось ранее, текущие решения приводят к тому, что файл 3 полностью пуст или просто содержит информацию из одного из двух файлов (но не из обоих).
Пожалуйста, попробуйте следующее:
awk '
# find the closest element in "a" to val and return the index
function binsearch(a, val, len,
low, high, mid) {
if (val < a[1])
return 1
if (val > a[len])
return len
low = 1
high = len
while (low <= high) {
mid = int((low + high) / 2)
if (val < a[mid])
high = mid - 1
else if (val > a[mid])
low = mid + 1
else
return mid
}
return (val - a[low]) < (a[high] - val) ? high : low
}
NR == FNR {
time[FNR] = $1
position[FNR] = $2
intensity[FNR] = $3
len++
next
}
{
i = binsearch(time, $1, len)
print $1 " " time[i] " " $2 " " position[i] " " intensity[i]
}
' file2.txt file1.txt
Результат:
125051.354948 125051.112784 058712.429 4413.34369 07629.87606
125052.352475 125052.100811 058959.934 4413.34371 07629.87605
125054.354322 125054.107361 058842.619 4413.34373 07629.87605
125055.352671 125055.107038 058772.045 4413.34375 07629.87604
125057.351794 125057.097928 058707.281 4413.34378 07629.87603
125058.352678 125058.098475 058758.959 4413.34378 07629.87606
Обратите внимание, что 4-е и 5-е значения в ожидаемом результате могут быть неправильно скопированы и вставлены.
[Как это работает]
Ключом является функция бинпоиск, которая находит ближайшее значение в массив и возвращает индекс массива. я бы не упоминал о алгоритм подробно, потому что это распространенный метод «бинарного поиска».
#!/bin/bash
if [[ $# -lt 2 ]]; then
echo "wrong args, it should be $0 file1 file2"
exit 0
fi
# clear blanks, add an extra column 'm' to file1, merge file1, file2, sort
{ awk 'NF{print $0, "m"}' "$1" ; awk 'NF' "$2"; } | sort -nk1,1 | \
\
awk '# record lines and fields in to a
{a[NR] = $0; a[NR,1] = $1; a[NR,2] = $2; a[NR,3] = $3}
END{
for(i=1; i<= NR; ++i){
# 3rd filed of file1 is "m"
if (a[i, 3] == "m"){
# get difference of column1 between current record ,previous record, next record
prevDiff = (i-1) in a && a[i-1,3] == "m" ? -1 : a[i,1] - a[i-1,1]
nextDiff = (i+1) in a && a[i+1,3] == "m" ? -1 : a[i+1,1] - a[i,1]
# compare differences, choose the close one and print.
if (prevDiff !=-1 && (nextVal == -1 || prevDiff < nextDiff))
print a[i,1], a[i-1, 1], a[i, 2], a[i-1, 2], a[i-1, 3]
else if (nextDiff !=-1 && (prevDiff == -1 || nextDiff < prevDiff))
print a[i,1], a[i+1, 1], a[i, 2], a[i+1, 2], a[i+1, 3]
else
print a[i]
}
}
}'
Выход { awk 'NF{print $0, "m"}' "$1" ; awk 'NF' "$2"; } | sort -nk1,1
:
125050.105886 4413.34358 07629.87620
125051.112784 4413.34369 07629.87606
125051.354948 058712.429 m
125052.100811 4413.34371 07629.87605
125052.352475 058959.934 m
125053.097826 4413.34373 07629.87603
125054.107361 4413.34373 07629.87605
125054.354322 058842.619 m
125055.107038 4413.34375 07629.87604
125055.352671 058772.045 m
125056.093783 4413.34377 07629.87602
125057.097928 4413.34378 07629.87603
125057.351794 058707.281 m
125058.098475 4413.34378 07629.87606
125058.352678 058758.959 m
125059.095787 4413.34376 07629.87602