การรวมสองไฟล์ด้วยตัวระบุเฉพาะ


9

ฉันมีสองไฟล์ที่มีรายการประมาณ 12900 และ 4400 ตามลำดับที่ฉันต้องการเข้าร่วม ไฟล์ดังกล่าวมีข้อมูลตำแหน่งสำหรับสถานีสำรวจสภาพอากาศที่มีที่ดินเป็นฐานทั่วโลก ไฟล์ที่ใหญ่ที่สุดจะได้รับการอัพเดตทุกสองสัปดาห์และมีขนาดเล็กลงปีละครั้งหรือมากกว่านั้น สามารถดูไฟล์ต้นฉบับได้ที่นี่ ( http://www.wmo.int/pages/prog/www/ois/volume-a/vola-home.htmและ http://weather.rap.ucar.edu/surface/ สถานี . txt ) ไฟล์ที่ฉันมีอยู่แล้วถูกจัดการโดยฉันด้วย awk, sed, และ bash script บางตัว ฉันใช้ไฟล์เพื่อให้เห็นภาพข้อมูลโดยใช้แพ็คเกจ 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 และตัวระบุที่สอง 4 ตัว (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

ฉันต้องการเข้าร่วมทั้งสองไฟล์เพื่อให้ไฟล์ที่ได้จะมีตัวระบุ 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

เป็นไปได้หรือไม่ที่จะทำภารกิจนี้ให้สำเร็จด้วยสคริปต์ทุบตีและ / หรือ awk


ไฟล์ที่เรียงลำดับตามฟิลด์ ID คืออะไร
miracle173

คำตอบ:


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

ทางออกที่สง่างามซึ่งฉันมีทักษะพื้นฐานเพียงเล็กน้อยเท่านั้นที่เข้าใจ
Staffan Scherloff

โปรแกรมอ่านหนึ่งไฟล์ในหน่วยความจำก่อนที่จะเริ่มทำงาน หากไฟล์มีขนาดใหญ่ขึ้นก็สามารถลดประสิทธิภาพลงอย่างมาก สำหรับไฟล์ขนาดใหญ่วิธีนี้ไม่สามารถใช้ได้
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ซึ่งบอกว่าฉันใช้ผิดอย่างเช่นในบางวิธีของ Frankenstein แต่เราทุกคนเรียนรู้ที่นี่ดังนั้นตกลง ... ฉันเดา

สวิตช์-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

โปรดเป็นคนใจดีเนื่องจากนี่เป็นงานที่อยู่ระหว่างดำเนินการและเราพยายามแสดงให้เห็นว่าใครจะแก้ปัญหาประเภทนี้โดยใช้joinคำสั่งได้เนื่องจากสิ่งนี้ดูเหมือนจะเป็นประเภทของปัญหา

ปัญหาที่โดดเด่น

  1. คอลัมน์ที่ 3

    สิ่งสำคัญคือวิธีการต่อสู้กับคอลัมน์ที่ 3 เนื่องจากเป็นการผสมผสานของ 1 คำและ 2 คำ ดูเหมือนว่าจะเป็นสิ่งสำคัญที่ทำให้joinฉันสะดุดและฉันไม่สามารถหาทางรอบ ๆ มันได้ คำแนะนำใด ๆ ที่จะได้รับการชื่นชม

  2. ระยะห่าง

    ระยะห่างดั้งเดิมทั้งหมดหายไปด้วยjoinและฉันไม่เห็นวิธีที่จะเก็บมันไว้ ดังนั้นjoinอาจไม่ใช่วิธีที่เหมาะสมในการจัดการกับปัญหาประเภทนี้หลังจากทั้งหมด

  3. ดูเหมือนว่าจะทำงานอย่างไร

    หลังจากการดัดด้วยบรรทัดคำสั่งวิธีแก้ปัญหาทั่วไปจะมีอยู่ดังนั้นสิ่งนี้จึงดูเหมือนว่ามันสามารถทำงานได้อย่างน้อยบางส่วนดังนั้นจึงสามารถใช้ที่แกนกลางของโซลูชันจากนั้นใช้เครื่องมืออื่น ๆ เช่นawkและsedทำความสะอาด . ขอร้องนี้คำถามที่ว่า: "ถ้าคุณกำลังทำความสะอาดมันขึ้นกับawkและsedวิธีการใด ๆ แล้วคุณเช่นกันอาจจะเพียงแค่ใช้พวกเขาโดยตรง?"


@Terdon - ดูคำตอบนี้แจ้งให้เราทราบว่าคุณคิดอย่างไร
slm

+1 ฉันคิดว่าเป็นเครื่องมือ unix ที่ได้รับการตั้งโปรแกรมให้แก้ภาระงานดังกล่าว
miracle173

ทำไมไม่ -e "" แทน -e "N / A" มันใช้งานไม่ได้ (ฉันลองไม่ได้)?
miracle173

@ miracle173 - โดยทั้งหมดใช่การใช้พื้นที่เป็น ARG ทำงานเกินไป ฉันเลือกใช้ 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

@ 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นี้เพื่อดูรายละเอียดว่าเกิดอะไรขึ้นกับ "hash" Bash 4 สนับสนุนอาเรย์ของเนทีฟ แต่มันควรจะทำงานใน 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เพื่อส่งออกเฉพาะฟิลด์ที่ 2 ของ file2
ดังนั้น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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.