Benzersiz tanımlayıcı ile iki dosyayı birleştirme


9

Katılmak istediğim sırasıyla yaklaşık 12900 ve 4400 girişli iki dosyam var. Dosyalar, dünyadaki tüm kara tabanlı hava gözlem istasyonları için konum bilgileri içerir. En büyük dosya iki haftada bir güncellenir ve yılda bir kez daha küçüktür. Orijinal dosyaları burada bulabilirsiniz ( http://www.wmo.int/pages/prog/www/ois/volume-a/vola-home.htm ve http://weather.rap.ucar.edu/surface/ stations.txt ). Sahip olduğum dosyalar zaten karışık awk, sed ve bash betiğiyle manipüle edildi. Dosyaları, Unidata'dan ücretsiz olarak temin edilebilen GEMPAK paketini kullanarak verileri görselleştirmek için kullanıyorum. En büyük dosya GEMPAK ile çalışır, ancak tam kapasitesiyle çalışmaz. Bunun için birleştirme gereklidir.

Dosya 1, ilk 6 rakamın benzersiz istasyon tanımlayıcısı olduğu hava gözlem istasyonları için konum bilgileri içerir. Farklı parametreler (istasyon numarası, istasyon adı, ülke kodu, enlem boylam ve istasyon yüksekliği) yalnızca hattaki konumu ile tanımlanır, yani sekme yok.

         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

Dosya 2, Dosya 1'deki benzersiz tanıtıcıyı ve ikinci, 4 karakterli tanıtıcıyı (ICAO konumlandırıcısı) içerir.

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

Ben iki dosya katılmak istiyorum, böylece sonuçta dosya 4 karakter tanıtıcısı satırındaki ilk 4 konumda olacak, yani tanımlayıcı 4 boşluk yerine gerekir.

         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

Bu görevi bir bash ve / veya awk betiği ile gerçekleştirmek mümkün müdür?


dosyalar kimlik alanına göre sıralanmış mı?
miracle173

Yanıtlar:


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

Sadece bazı temel becerilere sahip olduğum zarif bir çözüm. Teşekkür ederim!
Staffan Scherloff

Program çalışmaya başlamadan önce bir dosyayı belleğe okur. Dosyalar büyürse performansı önemli ölçüde düşürebilir. Daha büyük dosya için bu yöntem kullanılamaz.
miracle173

7

Birkaçımız bu sorunu joinsadece kullanarak çözüp çözemeyeceğimizi görmek istedik . Bunu benim denemem bu. Kısmen çalıştığı için @Terdon bana bir akşam yemeği borçludur 8-).

Komuta

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

Misal

$ 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

ayrıntılar

Yukarıdaki, joinbağırsaklarıma bir çeşit Frankenstein tarzında olduğu gibi yanlış kullandığımızı söyleyen hemen hemen her seçeneği kullanıyor, ama hepimiz burada öğreniyoruz, bu yüzden tamam ... Sanırım.

Anahtar -a1, birleştirme işlemine dosya1'deki dosya2'den karşılık gelen eşleşmesi olmayan satırları içermesini söyler. İşte bu satırları görüntülenmeye iten şey bu:

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

-1 1Ve -2 1sütunlar, ağırlıklı olarak onların 1 sütun 2 dosyalarından satırları katılmak için hangi söylüyor. Bu -o ..., 2 dosyadan hangi sütunların hangi sırayla görüntüleneceğini söylüyor.

-e "N/A"Tarafından boş gördüğü alanlar için yazdırmak için bir yer tutucu değeri olarak dize "N / A" kullanmayı diyor join.

Son 2 argümanlar 2 dosya besliyoruz, file1& file2sıralandığını olarak komutunu katılmak.

Lütfen nazik olun, çünkü bu devam eden bir çalışmadır ve joinkomutla bu tür bir sorunu nasıl çözeceğini göstermeye çalışıyoruz , çünkü bu amaçlanan bir sorun türü gibi görünecektir.

Öne çıkan sorunlar

  1. 3. sütun

    Bunlardan en önemlisi, 1 kelime ve 2 kelime değerinin bir karışımı olduğu için 3. sütuna nasıl katılacağıdır. Bu büyük bir engel gibi görünüyor joinve bunun etrafında bir yol bulamıyorum. Herhangi bir rehberlik takdir edilecektir.

  2. aralığı

    Tüm orijinal boşluklar kaybolur joinve ben de onu korumanın bir yolunu göremiyorum. Sonuçta joinbu tür sorunlarla başa çıkmanın doğru yolu olmayabilir.

  3. Yine de çalışıyor gibi görünüyor?

    Çok komut hattı ile bükme sonra genel bir çözüm bu da en azından kısmen çalışabilir gibi, bu bir çözelti özünde kullanılabilir, böylece gibi, ve daha sonra aşağıdaki gibi araçlarla faydalanmak bu yüzden orada awkve sedbunu temizlemek . Bu şu soruyu akla getiriyor: "Eğer awk& ile sedherhangi bir şekilde temizliyorsanız , onları doğrudan mı kullanabilirsiniz?".


@Terdon - bu cevabı görün, ne düşündüğünüzü bana bildirin.
slm

+1 Bence böyle bir görev görevini çözmek için programlanmış unix aracı
miracle173

neden -e "N / A" yerine -e "" değil. bu çalışmıyor (deneyemiyorum)?
miracle173

@ miracle173 - elbette, evet bir alanın arg olarak kullanılması. çok işe yarıyor. N / A'yı seçtim, böylece ne yaptığı belliydi.
slm

2
@terdon - evet eğlenceli bir problem değildi, daha azı, birlikte çalışmaktan zevk aldım, umarım gelecekteki bazı problemler üzerinde birlikte çalışabiliriz. Hala bu cevabın sitede faydalı bir amaca hizmet edeceğini düşünüyorum çünkü çok fazla örnek bulamadım, joinşimdi internette bu var. 8-)
slm

4

Bunu kullanmak mümkün olmalı , joinancak boşlukları ve boş alanları doğru şekilde nasıl yazdıracağımı anlayamıyorum. Her neyse, bu küçük Perl betiği hile yapacak:

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

Bunu foo.plşu şekilde kaydedin ve aşağıdaki gibi çalıştırın:

$ 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

Bu mükemmel çalışıyor. Çok teşekkürler - koddaki açıklama metnini gerçekten takdir ediyorum.
Staffan Scherloff

@StaffanScherloff rica ederim. Bu, sorunuzu yanıtlarsa, sorunun yanıtlanmış olarak işaretlenmesi için lütfen kabul edildi olarak işaretleyin.
terdon

@StaffanScherloff ikinci düşüncede, garip olanı kabul et, çok daha iyi. - terdon 5 dakika önce
terdon

@terdon - katılmakla artık bununla uğraştınız mı? Gitmek için bir yol gibi görünüyordu, daha -oönce özelliğini hiç kullanmamıştım, beklediğim gibi çalışmıyor.
slm

hayır @slm, ben farklı kombinasyonlarının düşünüyordum -ove -eancak dosya2'nin içinde herhangi bir giriş yok satırları yazdırmak için alamadım. İyi şanslar, bunun mümkün olup olmadığını bilmek isterim.
terdon

3

Bash yapacak.

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

"Karma" ile neler olup bittiğinin ayrıntıları için bu SO cevabına bakın Bash 4, ilişkilendirilebilir dizinin doğal olarak desteklenmesine karşın, bu 3 + 4 (belki 2?)

Biçimlendirmenizi almak için satırı dosya1'den kesmeniz gerekebilir.


2

İşte bunu yapmak için basit bir yol join(+ birkaç araç daha) ve aralığı korumak. Her iki dosya da istasyon numarasına göre sıralanmış gibi görünür, bu nedenle ek sıralama gerekmez:

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

Borudan önceki kısım , cevabında kullanılan slm'ye çok benzer, bu yüzden tekrar üzerinden geçmeyeceğim. Tek fark kullanıyorum olmasıdır yerine dört boşluk dize girdi eksik alanlara ilişkin ve - çıkış Dosya2 sadece 2 alan için So üretir dört karakter çapında sütun (aşağıda görülebilir değil ama hiçbir şey EK sonra orada ** ve boş satırlar aslında dört boşluktur):-e " "-o 2.2
join -j1 -a1 -o 2.2 -e " " file1 file2

EKVG







EKGF
EKTS

EKYT



EKSN

sonra pastebunu (boşluk olarak ayırıcı olarak kullanarak) cutilk 5 karakterden | paste -d' ' - <(cut -c6- file1)
sonuç verdiğimiz dosya1'e getiririz.

         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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.