Menggabungkan dua file dengan pengidentifikasi unik

9

Saya memiliki dua file dengan masing-masing sekitar 12900 dan 4400 entri, yang ingin saya gabung. File tersebut berisi informasi lokasi untuk semua stasiun pengamatan cuaca darat di seluruh dunia. File terbesar diperbarui setiap dua minggu, dan yang lebih kecil setahun sekali atau lebih. File asli dapat ditemukan di sini ( http://www.wmo.int/pages/prog/www/ois/volume-a/vola-home.htm dan http://weather.rap.ucar.edu/surface/ station.txt ). File yang saya miliki sudah dimanipulasi oleh saya dengan beberapa campuran awk, sed, dan skrip bash. Saya menggunakan file untuk memvisualisasikan data menggunakan paket GEMPAK, yang tersedia secara bebas dari Unidata. File terbesar akan bekerja dengan GEMPAK, tetapi tidak dengan kemampuan penuhnya. Untuk ini diperlukan sebuah join.

File 1 berisi informasi lokasi untuk stasiun pengamat cuaca, di mana 6 digit pertama adalah pengidentifikasi stasiun yang unik. Parameter yang berbeda (nomor stasiun, nama stasiun, kode negara, garis lintang, dan ketinggian stasiun) hanya ditentukan oleh posisinya di garis, yaitu tidak ada tab.

         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

File 2 berisi pengidentifikasi unik dalam File 1 dan yang kedua, pengidentifikasi 4 karakter (ICAO locator).

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

Saya ingin bergabung dengan dua file, sehingga file yang dihasilkan akan memiliki pengidentifikasi 4 karakter dalam 4 posisi pertama di baris, yaitu pengidentifikasi harus mengganti 4 spasi.

         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

Apakah mungkin menyelesaikan tugas ini dengan skrip bash dan / atau awk?

Staffan Scherloff
sumber
Apakah file diurutkan berdasarkan bidang ID?
miracle173

Jawaban:

8
awk 'BEGIN { while(getline < "file2" ) { codes[$1] = $2 } }
     { printf "%4s%s\n", codes[$1], substr($0, 5) }' file1
pengguna46911
sumber
Solusi yang elegan, yang saya hanya mengerti dengan keterampilan dasar. Terima kasih!
Staffan Scherloff
Program membaca satu file ke dalam memori sebelum mulai bekerja. Jika file menjadi lebih besar maka dapat menurunkan kinerja secara signifikan. Untuk file yang lebih besar metode ini tidak dapat digunakan.
miracle173
7

Beberapa dari kami ingin melihat apakah kami dapat menyelesaikan masalah ini joinhanya dengan menggunakan . Ini adalah usaha saya untuk melakukan itu. Karena sebagian berfungsi @Terdon berhutang makan malam 8-).

Perintah

$ 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)

Contoh

$ 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

Detail

Di atas adalah memanfaatkan hampir semua opsi yang tersedia untuk joinmemberitahu saya bahwa kita menggunakannya salah, seperti dalam beberapa jenis cara Frankenstein, tapi kita semua belajar di sini, jadi tidak apa-apa ... Saya kira.

Switch -a1memberitahu join untuk memasukkan baris apa pun yang tidak memiliki kecocokan yang sesuai dari file2 di file1. Jadi inilah yang mendorong garis-garis ini untuk ditampilkan:

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

The -1 1dan -2 1mengatakan yang kolom untuk bergabung dengan garis dari 2 file pada, terutama kolom 1 mereka. Yang -o ...mengatakan kolom mana dari 2 file untuk ditampilkan dan dalam urutan mana.

Kata -e "N/A"mengatakan untuk menggunakan string "N / A" sebagai nilai tempat penampung untuk mencetak untuk bidang yang dianggap kosong oleh join.

2 argumen terakhir memberi makan 2 file, file1& file2sebagaimana diurutkan ke dalam perintah bergabung.

Mohon berbaik hati, karena ini adalah pekerjaan yang sedang berjalan dan kami berusaha menunjukkan bagaimana seseorang dapat memecahkan masalah jenis ini menggunakan joinperintah, karena ini tampaknya merupakan jenis masalah yang dimaksudkan.

Masalah luar biasa

  1. Kolom ke-3

    Yang utama adalah bagaimana bersaing dengan kolom ke-3 karena merupakan campuran dari 1 kata dan 2 nilai kata. Ini sepertinya penghalang utama bagi joinsaya dan saya tidak bisa mencari jalan keluar. Bimbingan apa pun akan dihargai.

  2. Jarak

    Semua spasi asli hilang dengan joindan saya tidak melihat cara untuk tetap menggunakannya. Jadi joinmungkin bukan cara yang tepat untuk menangani masalah-masalah seperti ini.

  3. Tampaknya bekerja?

    Setelah banyak membengkokkan dengan baris perintah solusi umum ada sehingga ini sepertinya bisa bekerja setidaknya sebagian, sehingga ini dapat digunakan pada inti dari solusi, dan kemudian menggunakan alat-alat lain seperti awkdan seduntuk membersihkannya . Ini menimbulkan pertanyaan: "Jika Anda membersihkannya dengan awk& sedcara apa pun, maka Anda mungkin hanya menggunakannya secara langsung?".

slm
sumber
@Terdon - lihat jawaban ini, beri tahu saya apa yang Anda pikirkan.
slm
+1 Saya pikir itu alat unix yang diprogram untuk menyelesaikan tugas tugas seperti itu
miracle173
mengapa tidak -e "" bukannya -e "N / A". apakah ini tidak berhasil (saya tidak bisa mencobanya)?
miracle173
@ miracle173 - dengan segala cara, ya penggunaan spasi sebagai argumen. tidak bekerja juga. Saya memilih N / A sehingga jelas apa yang dilakukannya.
slm
2
@terdon - ya itu adalah masalah yang menyenangkan, senang bekerja bersama-sama, semoga kita juga bisa mengatasi beberapa masalah di masa depan. Saya masih berpikir jawaban ini akan melayani tujuan yang bermanfaat di situs karena saya tidak dapat menemukan banyak contoh ekstrem joinjadi sekarang internet memiliki yang ini. 8-)
slm
4

Ini harus dimungkinkan menggunakan jointetapi saya tidak tahu cara membuatnya mencetak spasi dan bidang kosong dengan benar. Bagaimanapun, skrip Perl kecil ini akan melakukan trik:

#!/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; 
}

Simpan ini sebagai foo.pldan jalankan sebagai berikut:

$ 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
terdon
sumber
Ini bekerja sangat baik. Terima kasih banyak - Saya sangat menghargai teks penjelasan Anda dalam kode ini.
Staffan Scherloff
@StaffanScherloff Anda dipersilakan. Jika ini menjawab pertanyaan Anda, harap tandai sebagai diterima sehingga pertanyaan dapat ditandai sebagai dijawab.
terdon
@StaffanScherloff pada pemikiran kedua, terima yang awk, itu jauh lebih baik. - terdon 5 mnt lalu
terdon
@terdon - apakah Anda ribut dengan yang ini menggunakan join Tampak seperti cara untuk pergi, tidak pernah menggunakan -ofitur sebelumnya, tidak berfungsi seperti yang saya harapkan.
slm
@slm tidak, saya memikirkan beberapa kombinasi -odan -etetapi tidak bisa mencetak baris yang tidak ada entri di file2. Semoga beruntung, saya akan tertarik untuk mengetahui apakah itu mungkin.
terdon
3

Bash akan melakukannya.

#!/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

Lihat jawaban SO ini untuk perincian tentang apa yang terjadi dengan "hash" Bash 4 secara native mendukung array asosiatif, tetapi ini seharusnya bekerja dalam 3 + 4 (mungkin 2?)

Anda mungkin perlu memotong garis dari file1 untuk mendapatkan pemformatan.

Mat
sumber
2

Berikut cara mudah untuk melakukannya dengan join(+ beberapa alat lainnya) dan menjaga jaraknya. Kedua file tampaknya diurutkan berdasarkan nomor stasiun sehingga tidak diperlukan penyortiran tambahan:

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

Bagian sebelum pipa sangat mirip dengan apa yang digunakan slm dalam jawabannya jadi saya tidak akan membahasnya lagi. Satu-satunya perbedaan adalah bahwa saya menggunakan -e " "- string empat spasi sebagai pengganti untuk bidang input yang hilang dan -o 2.2hanya menghasilkan bidang ke-2 file2
Jadi join -j1 -a1 -o 2.2 -e " " file1 file2menghasilkan kolom empat-char-lebar (tidak terlihat di bawah tetapi tidak ada setelah EK ** dan baris kosong sebenarnya adalah empat spasi):

EKVG







EKGF
EKTS

EKYT



EKSN

kita kemudian pasteini (menggunakan spasi sebagai pembatas) ke file1 dari mana kita cut5 karakter pertama | paste -d' ' - <(cut -c6- file1)
hasil akhir:

         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
sumber