공유 요소(bash/awk)를 기반으로 행 병합

공유 요소(bash/awk)를 기반으로 행 병합

다음 파일이 있습니다.

a3 v2c
v5 a7
a9 v2c
v1c a3 a7c

원하는 출력(각 행에 중복 없음):

a3 a7c a9 v1c v2c
a7 v5

내가 원하는 것은 적어도 하나의 요소를 공유하는 행을 병합하는 것입니다. 행 2에서는 두 요소가 모두 고유하며 행은 있는 그대로(정렬된 순서로) 출력됩니다. 1행은 "v2c"를 3행과 공유하고 "a3"을 4행과 공유하므로 이 3개의 행이 결합되어 정렬됩니다. 공유 요소는 다른 열에 있을 수 있습니다.

대용량 파일(200,000줄)의 경우 코드가 매우 느립니다.

Lines=$(awk 'END {print NR}' $1)
bank=$1
while [ $Lines -ge 1 ]
    do
        echo "Processing line $Lines"
        awk -v line=$Lines 'NR == line' $bank | awk NF=NF RS= OFS="\n" | sort | uniq > Query.$Lines
        k=0
    while [[ $k != 1 ]]
        do
                if [[ $k != "" ]]
                    then
                        grep -f Query.$Lines $bank | awk '{gsub(/\t/,"\n")}1' | awk '{gsub(/ /,"\n")}1' | sort | uniq > Query1.$Lines
                        grep -f Query1.$Lines $bank | awk '{gsub(/\t/,"\n")}1' | awk '{gsub(/ /,"\n")}1' | sort | uniq > Query2.$Lines
                        grep -f Query2.$Lines $bank | awk '{gsub(/\t/,"\n")}1' | awk '{gsub(/ /,"\n")}1' | sort | uniq > Query3.$Lines
                        k=$(diff Query2.$Lines Query3.$Lines)
                            if [[ $k != "" ]]
                                then mv Query3.$Lines Query.$Lines
                            fi
                    else
                        awk NF=NF RS= OFS=" " Query3.$Lines >> $1.output.clusters
                        grep -v -f Query3.$Lines $bank > NotFound.$Lines
                        bank=NotFound.$Lines
                        k=1
                fi
    done
rm Query*
        Lines=$(( $Lines - 1 ))
done
exit
find . -maxdepth 1 -type f -size 0 -delete
rm NotFound.* Query.* Query1.* Query2.* Query3.*

저는 bash나 awk를 사용하는 더 간단하고 효율적인 솔루션이 있다고 믿습니다. 미리 감사드립니다!

답변1

배열의 배열 합계에 GNU awk 사용 sorted_in:

$ cat tst.awk
{
    for ( fldNrA=1; fldNrA<NF; fldNrA++ ) {
        fldValA = $fldNrA
        for ( fldNrB=fldNrA+1; fldNrB<=NF; fldNrB++ ) {
            fldValB = $fldNrB
            val_pairs[fldValA][fldValB]
            val_pairs[fldValB][fldValA]
        }
    }
}

function descend(fldValA,       fldValB) {
    if ( !seen[fldValA]++ ) {
        all_vals[fldValA]
        for ( fldValB in val_pairs[fldValA] ) {
            descend(fldValB)
        }
    }
}

END {
    PROCINFO["sorted_in"] = "@ind_str_asc"
    for ( fldValA in val_pairs ) {
        delete all_vals
        descend(fldValA)
        if ( fldValA in all_vals ) {
            sep = ""
            for ( fldValB in all_vals ) {
                printf "%s%s", sep, fldValB
                sep = OFS
            }
            print ""
        }
    }
}

$ awk -f tst.awk file
a3 a7c a9 v1c v2c
a7 v5

원래 답변:

이것은시작배열의 배열에 GNU awk 사용:

$ cat tst.awk
{
    for ( fldNr=1; fldNr<=NF; fldNr++ ) {
        fldVal = $fldNr
        fldVals_rowNrs[fldVal][NR]
        rowNrs_fldVals[NR][fldVal]
    }
}
END {
    for ( rowNr=1; rowNr<=NR; rowNr++ ) {
        noOverlap[rowNr]
    }

    for ( rowNrA in rowNrs_fldVals ) {
        for ( fldVal in rowNrs_fldVals[rowNrA] ) {
            for ( rowNrB in fldVals_rowNrs[fldVal] ) {
                if ( rowNrB > rowNrA ) {
                    overlap[rowNrA][rowNrB]
                    delete noOverlap[rowNrA]
                    delete noOverlap[rowNrB]
                }
            }
        }
    }

    for ( rowNrA in overlap ) {
        for ( rowNrB in overlap[rowNrA] ) {
            print "Values overlap between lines:", rowNrA, rowNrB
        }
    }

    for ( rowNr in noOverlap ) {
        print "All unique values in line:", rowNr
    }
}

$ awk -f tst.awk file
Values overlap between lines: 1 3
Values overlap between lines: 1 4
All unique values in line: 2

print "Values overlap between lines:", rowNrA, rowNrB거기에서 나는 행을 호출하여 값이 겹치는 모든 행 사이의 모든 공통 값을 찾고 다음 PROCINFO["sorted_in"]을 사용하여 특정 순서로 인쇄하는 (재귀 하강?) 함수를 구현해야 할 것으로 예상합니다. .

재귀 함수에 대한 정보를 문의하셨기 때문에댓글에서다음은 다양한 목적으로 사용되는 재귀 awk 함수의 예입니다(모든 함수에는 이름이 지정되지만 descend()이름은 중요하지 않습니다).

이 작업을 통해 이러한 함수를 작성하는 방법에 대한 아이디어를 얻을 수 있기를 바랍니다.

답변2

이를 수행하는 루비는 다음과 같습니다.

ruby -e '
require "set"
line_map=Hash.new { |h,k| h[k]=[] }
num_map=Hash.new { |h,k| h[k]=Set.new() }
bucket=Hash.new { |h,k| h[k]=Set.new() }
$<.each {|line| line_all=line.chomp.split
    line_all.each{|sym| line_map[sym] << $. }
    num_map[$.].merge(line_all)
}
line_map.each{|k,v| 
    bucket[num_map[v[0]].all?{|ks| v.length==1 && line_map[ks][0]==v[0]}] << k
}
puts bucket[false].sort.join(" ")
puts bucket[true].sort.join(" ")
' file 

인쇄:

a3 a7c a9 v1c v2c
a7 v5

관련 정보