Объединение двух файлов с уникальным идентификатором

9

У меня есть два файла с примерно 12900 и 4400 записями соответственно, к которым я хочу присоединиться. Файлы содержат информацию о местонахождении всех наземных станций наблюдения за погодой по всему миру. Самый большой файл обновляется раз в две недели, а меньший раз в год или около того. Оригинальные файлы можно найти здесь ( http://www.wmo.int/pages/prog/www/ois/volume-a/vola-home.htm и http://weather.rap.ucar.edu/surface/ station.txt ). Мои файлы уже обрабатываются мной с помощью смешанного скрипта awk, sed и bash. Я использую файлы для визуализации данных, используя пакет GEMPAK, который можно бесплатно получить в Unidata. Самый большой файл будет работать с GEMPAK, но только не с его полной возможностью. Для этого необходимо объединение.

Файл 1 содержит информацию о местоположении станций наблюдения за погодой, где первые 6 цифр являются уникальным идентификатором станции. Различные параметры (номер станции, название станции, код страны, долгота широты и высота места станции) определяются только ее положением в строке, т.е. без вкладок.

         060090 AKRABERG FYR                        DN  6138   -666     101
         060100 VAGA FLOGHAVN                       DN  6205   -728      88
         060110 TORSHAVN                            DN  6201   -675      55
         060120 KIRKJA                              DN  6231   -631      55
         060130 KLAKSVIK HELIPORT                   DN  6221   -656      75
         060160 HORNS REV A                         DN  5550    786      21
         060170 HORNS REV B                         DN  5558    761      10
         060190 SILSTRUP                            DN  5691    863       0
         060210 HANSTHOLM                           DN  5711    858       0
         060220 TYRA OEST                           DN  5571    480      43
         060240 THISTED LUFTHAVN                    DN  5706    870       8
         060290 GROENLANDSHAVNEN                    DN  5703   1005       0
         060300 FLYVESTATION AALBORG                DN  5708    985      13
         060310 TYLSTRUP                            DN  5718    995       0
         060320 STENHOEJ                            DN  5736   1033      56
         060330 HIRTSHALS                           DN  5758    995       0
         060340 SINDAL FLYVEPLADS                   DN  5750   1021      28

Файл 2 содержит уникальный идентификатор в файле 1 и второй четырехзначный идентификатор (локатор ИКАО).

060100 EKVG
060220 EKGF
060240 EKTS
060300 EKYT
060340 EKSN
060480 EKHS
060540 EKHO
060600 EKKA
060620 EKSV
060660 EKVJ
060700 EKAH
060780 EKAT

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

         060090 AKRABERG FYR                        DN  6138   -666     101
EKVG     060100 VAGA FLOGHAVN                       DN  6205   -728      88
         060110 TORSHAVN                            DN  6201   -675      55
         060120 KIRKJA                              DN  6231   -631      55
         060130 KLAKSVIK HELIPORT                   DN  6221   -656      75
         060160 HORNS REV A                         DN  5550    786      21
         060170 HORNS REV B                         DN  5558    761      10
         060190 SILSTRUP                            DN  5691    863       0
         060210 HANSTHOLM                           DN  5711    858       0
EKGF     060220 TYRA OEST                           DN  5571    480      43
EKTS     060240 THISTED LUFTHAVN                    DN  5706    870       8
         060290 GROENLANDSHAVNEN                    DN  5703   1005       0
EKYT     060300 FLYVESTATION AALBORG                DN  5708    985      13
         060310 TYLSTRUP                            DN  5718    995       0
         060320 STENHOEJ                            DN  5736   1033      56
         060330 HIRTSHALS                           DN  5758    995       0
EKSN     060340 SINDAL FLYVEPLADS                   DN  5750   1021      28

Возможно ли выполнить эту задачу с помощью некоторого сценария bash и / или awk?

Стаффан Шерлофф
источник
файлы отсортированы по полю идентификатора?
чудо173

Ответы:

8
awk 'BEGIN { while(getline < "file2" ) { codes[$1] = $2 } }
     { printf "%4s%s\n", codes[$1], substr($0, 5) }' file1
user46911
источник
Элегантное решение, которое я понимаю только с некоторыми базовыми навыками. Спасибо!
Staffan Scherloff
Программа считывает один файл в память, прежде чем он начинает работать. Если файлы становятся больше, это может значительно снизить производительность. Для больших файлов этот метод не может быть использован.
чудо173
7

Пара из нас хотела посмотреть, сможем ли мы решить эту проблему, используя joinтолько. Это моя попытка сделать это. Так как это частично работает, @Terdon должен мне обед 8-).

Команда

$ join -a1 -1 1 -2 1 -o 2.2 1.1 1.2 1.3 1.4 1.5 1.6 1.7 -e "N/A" \
     <(sort file1) <(sort file2)

пример

$ join -a1 -1 1 -2 1 -o 2.2 1.1 1.2 1.3 1.4 1.5 1.6 1.7 -e "N/A" <(sort file1) <(sort file2) | column -t
N/A   060090  AKRABERG          FYR         DN    6138  -666  101
EKVG  060100  VAGA              FLOGHAVN    DN    6205  -728  88
N/A   060110  TORSHAVN          DN          6201  -675  55    N/A
N/A   060120  KIRKJA            DN          6231  -631  55    N/A
N/A   060130  KLAKSVIK          HELIPORT    DN    6221  -656  75
N/A   060160  HORNS             REV         A     DN    5550  786
N/A   060170  HORNS             REV         B     DN    5558  761
N/A   060190  SILSTRUP          DN          5691  863   0     N/A
N/A   060210  HANSTHOLM         DN          5711  858   0     N/A
EKGF  060220  TYRA              OEST        DN    5571  480   43
EKTS  060240  THISTED           LUFTHAVN    DN    5706  870   8
N/A   060290  GROENLANDSHAVNEN  DN          5703  1005  0     N/A
EKYT  060300  FLYVESTATION      AALBORG     DN    5708  985   13
N/A   060310  TYLSTRUP          DN          5718  995   0     N/A
N/A   060320  STENHOEJ          DN          5736  1033  56    N/A
N/A   060330  HIRTSHALS         DN          5758  995   0     N/A
EKSN  060340  SINDAL            FLYVEPLADS  DN    5750  1021  28

подробности

Вышесказанное использует практически все доступные опции, joinкоторые говорят моему инстинкту, что мы используем его неправильно, как в каком-то смысле Франкенштейна, но мы все здесь учимся, так что все в порядке ... я думаю.

Переключатель -a1сообщает соединению включить все строки, которые не имеют соответствующего совпадения с файлом file2 в файле file1. Вот что заставляет эти строки отображаться:

N/A   060330  HIRTSHALS         DN          5758  995   0     N/A

-1 1И -2 1говорят , какие столбцы , чтобы присоединиться к линии из 2 -х файлов, в основном , их 1 - й колонны. -o ...Говорит , какие столбцы из 2 -х файлов для отображения и в каком порядке.

-e "N/A"Говорит , чтобы использовать строку «N / A» в качестве значения держателя места для печати для полей, которые считаются пустыми по join.

Последние 2 аргумента подают 2 файла file1и file2сортируются в команде соединения.

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

Нерешенные вопросы

  1. 3-я колонна

    Главный из них - как бороться с 3-м столбцом, так как он представляет собой смесь значений из 1 слова и 2 слов. Это кажется серьезным камнем преткновения, joinи я не могу найти способ обойти это. Любое руководство будет оценено.

  2. Разнос

    Весь первоначальный интервал потерян, joinи я не вижу способа его сохранить. Так joinчто, в конце концов, это может быть неправильный способ решения подобных проблем.

  3. Кажется, работает, хотя?

    После долгих изгибов с командной строкой появляется общее решение, поэтому кажется, что оно может работать хотя бы частично, поэтому его можно использовать в основе решения, а затем использовать другие инструменты, такие как awkи sedдля его очистки. , Тем не менее, возникает вопрос: «Если вы убираете это с помощью awk& sedлюбым способом, то вы могли бы просто использовать их напрямую?».

SLM
источник
@ Тердон - посмотри на этот ответ, дай мне знать, что ты думаешь.
SLM
+1 Я думаю, что это инструмент Unix, который был запрограммирован для решения такой задачи
чудо173
почему не -e "" вместо -e "N / A". это не работает (я не могу попробовать)?
чудо173
@ miracle173 - да, использование пробела в качестве аргумента. тоже работает. Я выбрал N / A, чтобы было очевидно, что он делает.
SLM
2
@terdon - да, это была забавная проблема, тем не менее, нам понравилось работать над ней вместе, надеюсь, мы сможем вместе поработать над некоторыми будущими проблемами. Я все еще думаю, что этот ответ послужит полезной цели на сайте, так как я не смог найти много крайних примеров, joinтак что теперь в Интернете есть такой. 8-)
СЛМ
4

Это должно быть возможно с помощью, joinно я не могу понять, как заставить его печатать пробелы и пустые поля правильно. В любом случае, этот маленький скрипт на Perl сделает свое дело:

#!/usr/bin/env perl

## Open file2, the one that contains the codes
## it is expected to be the 1st argument given to the script.
open($a,"$ARGV[0]"); 

## Read the number<=>code pairs into a hash (an associative array)
## called 'k'
while (<$a>) {
    chomp; @f=split(/\s+/); $k{$f[0]}=$f[1];
}

## Open file1, the one that contains the data
## it is expected to be the 2nd argument given to the script.
open($b,"$ARGV[1]"); 
## Go through the file
while (<$b>) {
    ## Split each line at white space into the array @f
    @f=split(/\s+/);  

    ## $f[1] is the 6 digit number that defines the different stations.
    ## If this number has an entry in the hash %k, if it was found
    ## in file2, replace the first 4 spaces with its value from the hash.
    s/^\s{4}/$k{$f[1]}/ if defined($k{$f[1]});

    ## Print each line of the file
    print; 
}

Сохраните это как foo.plи выполните следующее:

$ perl foo.pl file2 file1
         060090 AKRABERG FYR                        DN  6138   -666     101
EKVG     060100 VAGA FLOGHAVN                       DN  6205   -728      88
         060110 TORSHAVN                            DN  6201   -675      55
         060120 KIRKJA                              DN  6231   -631      55
         060130 KLAKSVIK HELIPORT                   DN  6221   -656      75
         060160 HORNS REV A                         DN  5550    786      21
         060170 HORNS REV B                         DN  5558    761      10
         060190 SILSTRUP                            DN  5691    863       0
         060210 HANSTHOLM                           DN  5711    858       0
EKGF     060220 TYRA OEST                           DN  5571    480      43
EKTS     060240 THISTED LUFTHAVN                    DN  5706    870       8
         060290 GROENLANDSHAVNEN                    DN  5703   1005       0
EKYT     060300 FLYVESTATION AALBORG                DN  5708    985      13
         060310 TYLSTRUP                            DN  5718    995       0
         060320 STENHOEJ                            DN  5736   1033      56
         060330 HIRTSHALS                           DN  5758    995       0
EKSN     060340 SINDAL FLYVEPLADS                   DN  5750   1021      28
Тердон
источник
Это работает отлично. Большое спасибо - я действительно ценю ваш поясняющий текст в коде.
Staffan Scherloff
@StaffanScherloff, пожалуйста. Если это отвечает на ваш вопрос, отметьте его как принятый, чтобы вопрос мог быть помечен как ответ.
Тердон
@StaffanScherloff, подумав вторым, примите awk, это намного лучше. - Тердон 5 минут назад
Тердон
@terdon - ты возился с этим больше, используя join? Похоже, путь, никогда не использовал его -oфункции раньше, не работает, как я ожидал.
SLM
@slm нет, я имел в виду какой - то комбинации -oи , -eно не мог заставить его печатать строки , которые не имели никаких записей в file2. Удачи, мне было бы интересно узнать, возможно ли это.
Тердон
3

Баш сделает.

#!/usr/bin/env bash

# ### create a psuedo hash of icao locator id's
# read each line into an array
while read -a line; do
  # set icao_nnnnnn variable to the value
  declare "icao_${line[0]}"=${line[1]}
done <file2


# ### match up icao id's from file1
# read in file line at a time
while IFS=$'\n' read line; do
  # split the line into array
  read -a arr <<< "$line"
  # if the icao_nnnnnn variable exists, it will print out
  var="icao_${arr[0]}"
  printf "%-8s %s\n" "${!var}" "$line"
done <file1

Смотрите этот SO-ответ, чтобы узнать подробнее о том, что происходит с «хэшем». Bash 4 изначально поддерживает ассоциативный массив, но это должно работать в 3 + 4 (возможно, в 2?)

Возможно, вам придется обрезать строку из файла file1, чтобы получить форматирование.

Matt
источник
2

Вот простой способ сделать это join(+ еще пара инструментов) и сохранить интервал. Оба файла отсортированы по номеру станции, поэтому дополнительная сортировка не требуется:

join -j1 -a1 -o 2.2 -e "    " file1 file2 | paste -d' ' - <(cut -c6- file1)

Часть перед каналом очень похожа на ту, что использовала slm в своем ответе, поэтому я не буду ее повторять. Единственное отличие состоит в том, что я использую -e " "- строку из четырех пробелов в качестве замены для пропущенных полей ввода и -o 2.2для вывода только 2-го поля файла2.
Таким образом, join -j1 -a1 -o 2.2 -e " " file1 file2создается столбец шириной в четыре символа (ниже он не виден, но после EK ничего нет ** и пустые строки на самом деле четыре пробела):

EKVG







EKGF
EKTS

EKYT



EKSN

Затем мы pasteэто (используя пробел в качестве разделителя) для file1, из которого мы cutпервые 5 символов | paste -d' ' - <(cut -c6- file1)
Конечный результат:

         060090 AKRABERG FYR                        DN  6138   -666     101
EKVG     060100 VAGA FLOGHAVN                       DN  6205   -728      88
         060110 TORSHAVN                            DN  6201   -675      55
         060120 KIRKJA                              DN  6231   -631      55
         060130 KLAKSVIK HELIPORT                   DN  6221   -656      75
         060160 HORNS REV A                         DN  5550    786      21
         060170 HORNS REV B                         DN  5558    761      10
         060190 SILSTRUP                            DN  5691    863       0
         060210 HANSTHOLM                           DN  5711    858       0
EKGF     060220 TYRA OEST                           DN  5571    480      43
EKTS     060240 THISTED LUFTHAVN                    DN  5706    870       8
         060290 GROENLANDSHAVNEN                    DN  5703   1005       0
EKYT     060300 FLYVESTATION AALBORG                DN  5708    985      13
         060310 TYLSTRUP                            DN  5718    995       0
         060320 STENHOEJ                            DN  5736   1033      56
         060330 HIRTSHALS                           DN  5758    995       0
EKSN     060340 SINDAL FLYVEPLADS                   DN  5750   1021      28
don_crissti
источник