一意の識別子で2つのファイルを結合する


9

約12900エントリと4400エントリの2つのファイルがあり、それらを結合したいと思います。ファイルには、世界中のすべての陸上気象観測所の位置情報が含まれています。最大のファイルは隔週で更新され、小さいファイルは年に1回程度更新されます。元のファイルはここにあります(http://www.wmo.int/pages/prog/www/ois/volume-a/vola-home.htmおよび http://weather.rap.ucar.edu/surface/ station.txt)。私が持っているファイルは、いくつかのawk、sed、およびbashスクリプトが混在しているため、すでに操作されています。このファイルを使用して、Unidataから無料で入手できるGEMPAKパッケージを使用してデータを視覚化します。最大のファイルは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の一意の識別子と2番目の4文字の識別子(ICAOロケーター)が含まれています。

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

2つのファイルを結合して、結果のファイルの行の最初の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スクリプトでこのタスクを実行することは可能ですか?


ファイルはIDフィールドでソートされていますか?
miracle173 2013

回答:


8
awk 'BEGIN { while(getline < "file2" ) { codes[$1] = $2 } }
     { printf "%4s%s\n", codes[$1], substr($0, 5) }' file1

基本的なスキルだけで理解できるエレガントなソリューションです。ありがとうございます。
Staffan Scherloff 2013

プログラムは、動作を開始する前に1つのファイルをメモリに読み込みます。ファイルが大きくなると、パフォーマンスが大幅に低下する可能性があります。大きなファイルの場合、この方法は使用できません。
miracle173

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に含めるようにjoinに指示します。だから、これはこれらの線を表示するために動かしているものです:

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

-1 1そして-2 1上の2つのファイル、主に彼らの第一列から行を結合する列を言っています。これ-o ...は、2つのファイルのどの列をどの順序で表示するかを示しています。

-e "N/A"で、空とみなされるフィールドに印刷するにはプレースホルダ値として文字列「N / A」を使用すると言いますjoin

最後の2つの引数は、2つのファイルを供給している、file1file2に分類としてコマンドに参加します。

これは進行中の作業であり、joinコマンドを使用してこのタイプの問題をどのように解決するかを示すつもりです。

未解決の問題

  1. 3列目

    1ワードと2ワードの値が混在しているため、主なものは3列目との競合方法です。これは大きな障害のようで、joinどうすればいいのかわからない。どんなガイダンスもいただければ幸いです。

  2. 間隔

    元の間隔はすべて失われjoin、それを維持する方法もわかりません。だから、joinすべての後にこの種の問題に対処するための正しい方法ではないかもしれません。

  3. でも動作するようです?

    それは、少なくとも部分的に働くことができるように、一般的な解決策があるコマンドラインで曲げずっと後にこれは思えないので、これはソリューションの中核に使用し、その後のような他のツールを利用することができawkそしてsedそれをクリーンアップします。これは頼む質問かかわら:「あなたがそれを掃除している場合awksedどのような方法、そしてあなたにもちょうどそれらを直接使用することがあります?」。


@Terdon-この答えを見て、あなたの考えを教えてください。
slm

+1これは、そのようなタスクタスクを解決するようにプログラムされたUNIXツールだと思います
miracle173

-e "N / A"の代わりに-e ""を使用しないでください。これは機能しませんか(私は試すことができません)?
miracle173 2013

@ miracle173-はい、スペースを引数として使用しています。あまりにも動作します。私はN / Aを選択しました。
slm

2
@terdon-ええ、それはそれでも楽しい問題でした、一緒に取り組むのを楽しんだ、うまくいけば私たちはいくつかの将来の問題にも一緒に取り組むことができるでしょう。私はまだこの回答がサイトで役立つ目的だと思います。極端な例を見つけることができなかったjoinので、インターネットにはこれが含まれています。8
slm

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 2013

@StaffanScherloffどういたしまして。これで質問の回答が得られた場合は、承認済みのマークを付けて、質問に回答済みのマークを付けることができます。
terdon

@StaffanScherloffは考え直し、awkのものを受け入れます。– terdon 5分前
terdon

@terdon-ジョインを使用してこれでもう大騒ぎしましたか?進むべき道の-oように見え、以前にその機能を使用したことがなく、期待したように機能していません。
slm

ノー@slm、私はいくつかの組み合わせを考えていた-oし、-eそれはFILE2にエントリがなかった行を出力することができませんでした。幸運を祈ります。それが可能かどうか知りたいです。
terdon

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の回答を参照してください。Bash4は連想配列をネイティブでサポートしていますが、これは3 + 4(おそらく2?)で機能するはずです。

フォーマットを取得するには、file1から行を左トリミングする必要がある場合があります。


2

これはjoin(+いくつかのツール)でそれを行い、間隔を維持する簡単な方法です。両方のファイルはステーション番号でソートされているように見えるため、追加のソートは必要ありません。

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

パイプの前の部分はslm彼の回答で使用しものと非常に似ているので、これ以上は説明しません。唯一の違いは、私が使用しているということである-e " "行方不明の入力フィールドの代わりとして四つのスペースの文字列をと- -o 2.2出力にFILE2の唯一の2ndフィールド
そうはjoin -j1 -a1 -o 2.2 -e " " file1 file2四文字全体の列を(それがEK後、以下の目に見えるが、そこの何もないん生成します**および空の行は実際には4つのスペースです):

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.