Как объединить два файла .txt в один, сопоставляя каждую строку по метке времени, используя awk - PullRequest
1 голос
/ 29 мая 2019

Резюме:

В настоящее время у меня есть два файла .txt, импортированных из системы опроса, которую я тестирую. Столбец 1 каждого файла данных представляет собой временную метку в формате «HHMMSS.SSSSSS». В файле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
  • Результат: выводит только аналогичную версию исходного файла2

Я попытался перенастроить существующие 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"

Я смотрел на интеграцию некоторого вида регулярных выражений или команды split в сценарий ... но был озадачен тем, как действовать, и не придумал ничего существенного.

Пример данных

    $ 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 полностью пустой или просто содержит информацию из одного из двух файлов (но не из обоих)

Ответы [ 2 ]

0 голосов
/ 29 мая 2019
#!/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
0 голосов
/ 29 мая 2019

Пожалуйста, попробуйте следующее:

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-е значения в ожидаемом результате могут быть ошибочно скопированы и вставлены.

[Как это работает]

Ключом является функция binsearch , которая находит ближайшее значение в массиве и возвращает индекс в массив.Я бы не стал подробно рассказывать об алгоритме, потому что это распространенный метод «двоичного поиска».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...