값 사이의 명시적인 범위

값 사이의 명시적인 범위

bash 마법을 사용하여 텍스트 파일의 두 번째 열 요소 사이의 범위를 계산하는 깔끔한 방법이 있습니까? (저는 현재 Python을 사용하여 이 작업을 수행하고 있습니다.)

입력: 파일 1

A   1-5
A   17-19
B   1-5
B   4-6

예상 출력: 파일 2

A   1,2,3,4,5,17,18,19
B   1,2,3,4,5,6    

편집하다@Anthon: 요소를 축적하기 위해 다음과 같은 것을 사용하고 있습니다. 그런 다음 for 루프를 사용하여 범위를 계산합니다.

d_pos= {} 
for row in open('File.txt'): 
    x, y = [ value.strip() for value in row.split('\t')] 
    if x in d_pos:        
        d_pos[x].append(y)    
    else:        
        d_pos[x] = [y]

답변1

귀하가 요청한 대로 직접 bash를 사용합니다(그러나 저는 bash 4.0이 필요한 연관 배열을 사용하고 있습니다).

비결은중괄호 시퀀스 확장표현식 {x..y}, 정수 x의 경우 모든 y는 포함된 값 범위(예: [x,y])에 대한 텍스트 목록으로 확장됩니다. eval변수 확장 전에 중괄호 확장이 발생하므로 하나도 추가해야 합니다 .

declare -A data seen  # explicit associative arrays
while read col range; do
   data[$col]="${data[$col]} $(eval echo {${range/-/..}})"
done <<DATA
A   1-5
A   17-19
B   1-5
B   4-6
DATA

# dump array
#declare -p data

for ii in ${!data[@]}; do
    seen=();  datum=""
    # build list of unique values
    for dd in ${data[$ii]}; do
        (( ${seen[$dd]:-0} )) || datum="$datum $dd"
        let seen[$dd]++
    done

    datum=${datum# }     # drop leading space
    datum=${datum// /,}  # spaces to commas
    printf "%-4s %s\n" "$ii" "$datum"
done

시퀀스 확장의 변형은 a{x..y}b확장의 각 항목 앞에 "a"를 추가하고 "b"를 추가하는 것입니다. 이를 사용하여 ","를 추가하고 필요에 따라 데이터 변수를 변경할 수 있습니다. 시퀀스 확장은 1의 증분을 처리하거나 x > y인 경우 -1을 처리합니다.

출력을 정렬해야 할 수도 있습니다. 반복 연관 배열의 키에 대해 잘 정의된 순서가 없으며 입력 범위가 사전 정렬되었는지 여부를 밝히지 않았습니다(그래서 코드를 너무 복잡하게 만들지 않았습니다).

답변2

예를 들어 Python 코드는 가깝지만 항목 B의 4와 5가 겹치는 것을 처리할 수 없습니다.

다음은 겹침을 방지하기 위해 a 를 사용하고 입력 줄에 키가 이미 존재하는 경우 명시적 테스트를 제거하기 위해 setdefault 를 사용하여 문자에 대한 의존도를 줄이고 명시 set()적 테스트를 제거함으로써 문제를 올바르게 처리합니다 .d_pos.split()\t.strip()

d_pos= {}
for row in open('File.txt'):
    x, y = [ value for value in row.split()]
    y1, y2 = map(int, y.split('-'))
    d_pos.setdefault(x, set()).update(range (y1, y2+1))
for x in sorted(d_pos):
    print '{}\t{}'.format(x, ','.join(map(str, d_pos[x])))

답변3

사용할 수 있는 경우 perl:

$ perl -MList::MoreUtils=uniq -anle '
    ($s,$e) = split "-", $F[1];
    push @{$h{$F[0]}}, $s..$e; 
    END {
        $" = ",";
        print "$_   @{[uniq@{$h{$_}}]}" for keys %h;
    }
' file
A   1,2,3,4,5,17,18,19
B   1,2,3,4,5,6

List::MoreUtils핵심에 없기 때문에 사용하고 싶지 않다면 다음을 수행할 수 있습니다.

$ perl -anle '
    ($s,$e) = split "-", $F[1];
    push @{$h{$F[0]}}, $s..$e; 
    END {
        $" = ",";
        for $k (keys %h) {
            %u=();
            print "$k   @{[grep {!$u{$_}++} @{$h{$k}}]}";
        }
    }
' file

관련 정보