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

Резюме:

В настоящее время у меня есть два файла .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
  • Результат: выводит только аналогичную версию исходного файла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"

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

Образец данных

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

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
351
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

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

Другие вопросы по теме