2개의 csv 파일의 여러 열을 비교하고 하나를 다른 파일의 열로 바꿉니다.

2개의 csv 파일의 여러 열을 비교하고 하나를 다른 파일의 열로 바꿉니다.

2개의 파일을 비교 file a하고 file b동일한 열을 사용해야 하지만 열 값 비교와 같이 2개의 파일에 무작위로 배치해야 하며 , 임의 account의 행 에 대해 일치하는 항목이 발견되면 열 1을 from으로 바꾸고 모두 표시 합니다 . 일치하는 레코드는 다음과 같습니다.codedatetypepcvolbsaccountfile aaccount1file bfile afile c

나는 Linux 배경 지식이 없으며 온라인 포럼을 검색한 후 AWK로 이 작업을 수행할 수 있다는 것을 조금 알고 있지만 awk에는 익숙하지 않습니다. 도와주세요. Linux 환경에서 비교가 필요합니다.

무작위로 배치된 열은 명령에서 파일 a와 파일 b를 일치시키는 데 사용할 수 있는 열을 제공할 수 있어야 함을 의미합니다(열의 순서는 두 파일에서 동일하지 않습니다). 두 파일 모두에서 일치하는 열은 8개, 10개 또는 15개일 수 있습니다.

계정과 유사하게 account1은 첫 번째 및 마지막 열이 아니어야 하며 명령은 업데이트해야 하는 열을 선택할 수도 있어야 합니다.

반복 처리 -->파일 b에 파일 a의 1개 레코드에 대한 중복이 많은 경우 파일 b의 첫 번째 일치 레코드를 업데이트하여 일치에서 다시 사용할 수 없도록 해야 합니다. -->파일 a에 중복이 많은 경우 파일 b와 일치하는 레코드가 1개만 있으면 최종 파일 c는 파일 a의 일치하는 값 레코드를 모두 업데이트하는 대신 계정 값 1개만 업데이트해야 합니다.

file a

account,temp1,code,type,date,subtask,pc,toy,vol,bs,sub
6576,WEQR,TYRE,BS,54122022,OBCD,K,BAT,5000,F,SCSC
1234,GFHD,ASDF,BS,21122022,STOP,C,CAT,1000,S,MATH
7654,GHAD,LOPI,CV,9089022,KGAD,G,BSEE,5908,J,IOYU

file b

account,code,date,type,inst,insttype,pc,str,vol,bs,name,xdate,account1
1234,ASDF,21122022,BS,GOLDY,RUB,C,123.1,1000,S,RON,90891234,CCCCC
2761,LOPCS,10122022,BSFD,SLV,STR,C,123.9,1001,B,RON,99999988,DDDDD
0980,RTDF,28822025,JUFG,BRNZ,HIY,C,123.8,2000,S,RON,88881234,EEEEE

file c

account,temp1,code,type,date,subtask,pc,toy,vol,bs,sub
6576,WEQR,TYRE,BS,54122022,OBCD,K,BAT,5000,F,SCSC
CCCCC,GFHD,ASDF,BS,21122022,STOP,C,CAT,1000,S,MATH
7654,GHAD,LOPI,CV,9089022,KGAD,G,BSEE,5908,J,IOYU

답변1

이 답변은 다음을 사용하는 솔루션으로 시작됩니다.밀러csvsql, Miller의 솔루션과 함께 Miller를 계속 사용하십시오.csvkit, 그런 다음 사용되는 솔루션으로 끝납니다 csvsql.


사용밀러( mlr) 먼저 (왼쪽) 다음 명명된 필드의 데이터를 결합합니다 fileA.fileB

account,code,date,type,pc,vol,bs

...그런 다음 account1필드 이름을 바꿉니다 account(필드가 있는 레코드의 경우 account1결합된 레코드만 해당).

그런 다음 필드 순서를 변경하고 출력에서 ​​불필요한 필드를 제거합니다.

mlr --csv \
    join -f fileA -j account,code,date,type,pc,vol,bs --ul then \
    rename account1,account then \
    cut -o -f account,temp1,code,type,date,subtask,pc,toy,vol,bs,sub fileB

질문의 데이터 출력을 제공합니다.

account,temp1,code,type,date,subtask,pc,toy,vol,bs,sub
CCCCC,GFHD,ASDF,BS,21122022,STOP,C,CAT,1000,S,MATH
6576,WEQR,TYRE,BS,54122022,OBCD,K,BAT,5000,F,SCSC
7654,GHAD,LOPI,CV,9089022,KGAD,G,BSEE,5908,J,IOYU

두 입력 파일의 필드 순서는 관련이 없습니다.

조인에 사용할 수 있는 필드를 모르는 경우 공통 필드 이름을 개별적으로 계산할 수 있습니다(불행히도 Miller는 "자연 조인" 작업을 수행할 수 없지만 조인할 필드 이름의 명시적인 목록을 제공해야 합니다).

mlr --csv put -q '
    if (NR == 1) {
        for (k in $*) { @f[k] = 1 }
    } else {
       for (k in @f) {
           is_null($[k]) { unset @f[k] }
       }
    }
   end {
       common_fieldnames = joink(@f,",");
       emit common_fieldnames
   }' fileA fileB

주어진 데이터에 대해 다음 CSV 데이터세트를 출력합니다.

common_fieldnames
"account,code,type,date,pc,vol,bs"

도착하다오직--csv및 와 결합된 것처럼 헤더가 없고 따옴표가 없는 CSV 출력을 생성하는 옵션을 사용하여 쉼표로 구분된 목록을 가져옵니다 .--headerless-csv-output--quote-none


완전히 다른 접근 방식 csvsqlcsvkit자연 왼쪽 조인을 수행한 다음 mlr출력을 사후 처리하는 데 사용됩니다.

csvsql --query 'SELECT * FROM "fileA" NATURAL LEFT JOIN "fileB"' fileA fileB |
mlr --csv \
    put 'is_not_null($account1) { $account = $account1 }' then \
    cut -o -f account,temp1,code,type,date,subtask,pc,toy,vol,bs,sub

이렇게 하면 두 파일 사이에 어떤 필드가 공통인지 걱정할 필요가 없습니다.

원하는 경우 SQL을 사용하여 모든 작업을 수행할 수도 있습니다.

csvsql --query '
    CREATE TEMPORARY TABLE tmp AS SELECT * FROM "fileA" NATURAL LEFT JOIN "fileB";
    UPDATE tmp SET account = account1 WHERE account1 IS NOT NULL;
    SELECT account,temp1,code,type,date,subtask,pc,toy,vol,bs,sub FROM tmp;' fileA fileB

답변2

#!/bin/bash

IFS=, hdrs_to_check="$*"
col_to_change="account"
col_with_value="account1"

awk -F, -v hdrs_to_check="$hdrs_to_check" \
        -v col_to_change="$col_to_change" \
        -v col_with_value="$col_with_value" '

function make_compound_key(hdrs_2_idx_map) {
    key = ""
    for (i = 1; i <= hdrs_to_check_arr_size; i++) {
        hdr_name = hdrs_to_check_arr[i]
        hdr_idx = hdrs_2_idx_map[hdr_name]
        key = key$hdr_idx","
    }
    return key
}

BEGIN {
    hdrs_to_check_arr_size = split(hdrs_to_check, hdrs_to_check_arr, ",")
}

#Both file headers processing
FNR == 1 {
    #Map file_a and file_b headers to their indexes.
    for (i = 1; i <= NF; i++) {
        if (NR == 1) {
            f_b_hdr_2_idx_map[$i] = i
        } else {
            f_a_hdr_2_idx_map[$i] = i
        }
    }
    next
}

#file_b processing
FNR == NR {
    key = make_compound_key(f_b_hdr_2_idx_map)

    # Duplicate keys are not changing the "account1" value, it is fixed
    # from the first key occurence.
    if (!(key in file_b_dct)) {
        file_b_dct[key] = $f_b_hdr_2_idx_map[col_with_value]
    }

    next
}

#file_a processing
FNR != NR {
    key = make_compound_key(f_a_hdr_2_idx_map)

    # "file_a_updated_dct" is needed to prevent following lines
    # with same "key" be changed. These lines are left unchanged.
    if ((key in file_b_dct) && !(key in file_a_updated_dct)) {
        $f_a_hdr_2_idx_map[col_to_change] = file_b_dct[key]         
        file_a_updated_dct[key]
    }

    print
}
' file_b.txt OFS=, file_a.txt
#' file_b.txt OFS=, file_a.txt | column -s, -t

용법:

$ ./my_script.sh account code date type pc vol bs

관련 정보