부모-자식 관계를 얻기 위해 분리된 문자열을 피벗 해제합니다.

부모-자식 관계를 얻기 위해 분리된 문자열을 피벗 해제합니다.

부모-자식 관계를 달성하기 위해 데이터를 반전해야 하는 시나리오가 있습니다. 내 소스 데이터는 다음과 같습니다.

Key_Col|계층

1|a, b, c, d

2|a, b, c, d, e

내 예상 결과는 다음과 같습니다.

주요 학교 어린이 부모
1
1 두번째
1 두번째
1 유효하지 않은
2 이자형
2
2 두번째
2 두번째
2 유효하지 않은

bash 스크립트를 통해 이를 달성하는 방법을 알려주시겠습니까?

내가 사용하는 스크립트는 다음과 같습니다.

Var="1|a,b,c,d";
for i in $Var
do
 Key=`echo $i |cut -d'|' -f1`
 Hierarchy=`echo $i |cut -d'|' -f2`
 Delim_Count=`echo ${Hierarchy} |awk -F',' '{ print NF-1 }'`
 for (( c=$Delim_Count+1; c>=1; c-- ))
 do
   Parent=`echo ${Hierarchy} |cut -d',' -f$c`
   Prev=`expr $c - 1`
   if [ $Prev -ne 0 ]; then
    Child=`echo ${Hierarchy} |cut -d',' -f${Prev}`
    echo "${Key}|${Parent}|${Child}"
   else
    echo "${Key}|${Parent}|"
   fi
 done
done

그런데 문제는 100줄을 넘으면 스크립트를 완성하는데 오랜 시간이 걸린다는 점이다.

답변1

이런 종류의 일은 일반적으로 텍스트나 구조화된 데이터를 처리하도록 설계된 언어를 사용하면 더 쉽습니다. 표준 텍스트 처리 유틸리티를 사용한 솔루션 awk및 사용법은 다음과 같습니다.밀러( mlr)는 구조화된 데이터(데이터는 CSV 형식) 작업을 위해 특별히 설계된 도구입니다.


그리고 awk:

$ cat file
Key_Col|Hierarchy
1|a,b,c,d
2|a,b,c,d,e
$ awk 'BEGIN { OFS=FS="|" } NR == 1 { print $1, "Child", "Parent"; next } { n=split($2,a,","); a[0]="null"; for (i=n;i>0;i--) print $1,a[i],a[i-1] }' file
Key_Col|Child|Parent
1|d|c
1|c|b
1|b|a
1|a|null
2|e|d
2|d|c
2|c|b
2|b|a
2|a|null

위의 코드는 각 입력 라인을 구분된 필드 awk집합으로 읽습니다 . |쉼표의 두 번째 필드를 배열로 분할합니다 a. 배열의 0번째 요소는 문자열 null(split()첫 번째 인덱스 1을 사용하여 배열 만들기, 따라서 데이터를 덮어쓰지 않고 인덱스 0을 사용할 수 있음을 알 수 있습니다. 그런 다음 배열의 끝에서 시작까지 반복하여 첫 번째 필드의 값과 현재 배열 요소 및 배열의 ​​이전 요소를 출력합니다. 마지막 반복에 도달하면 루프 변수의 값은 1이 되어 a[1]a[0]()가 인쇄됩니다.null

제목이 포함된 입력의 첫 번째 줄은 다르게 처리됩니다. 분할 등을 하는 대신 코드는 입력의 첫 번째 필드와 Child문자열 및 Parent. 조건부 NR==1블록이 이를 수행합니다.

가독성을 위해 코드 형식이 변경되었습니다 awk.

BEGIN {
    OFS = FS = "|"
}

NR == 1 {
    print $1, "Child", "Parent"
    next
}

{
    n = split($2, a, ",")
    a[0] = "null"
    for (i = n; i > 0; i--)
        print $1, a[i], a[i-1]
}

입력은 CSV처럼 보이므로 CSV 인식 도구를 사용하여 처리하는 것이 더 안전할 것입니다. Miller( mlr)는 다음과 같은 도구입니다.

$ mlr --csv --fs pipe put -q 'm=splitnv($Hierarchy,","); m[0]="null"; for (var i=length(m)-1;i>0;i-=1) { emit {"Key_Col": $Key_Col, "Child": m[i], "Parent": m[i-1] } }' file
Key_Col|Child|Parent
1|d|c
1|c|b
1|b|a
1|a|null
2|e|d
2|d|c
2|c|b
2|b|a
2|a|null

Miller put표현식은 위의 코드와 동일한 개요를 따르지만 awkMiller는 이러한 헤더를 읽고 사용하는 방법을 알고 있으므로 헤더를 특별한 경우로 처리할 필요가 없습니다.

m = splitnv($Hierarchy, ",")
m[0] = "null"

for (var i = length(m) - 1; i > 0; i -= 1) {
    emit {
        "Key_Col": $Key_Col,
        "Child": m[i],
        "Parent": m[i-1]
    }
}

putMiller를 사용하면 하위 명령 앞의 옵션을 조정하여 다양한 형태의 결과를 생성할 수 있습니다.

예쁘게 인쇄된 "금지된" 출력:

$ mlr --c2p --barred --ifs pipe put ...as above...
+---------+-------+--------+
| Key_Col | Child | Parent |
+---------+-------+--------+
| 1       | d     | c      |
| 1       | c     | b      |
| 1       | b     | a      |
| 1       | a     | null   |
| 2       | e     | d      |
| 2       | d     | c      |
| 2       | c     | b      |
| 2       | b     | a      |
| 2       | a     | null   |
+---------+-------+--------+

JSON:

$ mlr --c2j --ifs pipe put ...as above...
{ "Key_Col": 1, "Child": "d", "Parent": "c" }
{ "Key_Col": 1, "Child": "c", "Parent": "b" }
{ "Key_Col": 1, "Child": "b", "Parent": "a" }
{ "Key_Col": 1, "Child": "a", "Parent": "null" }
{ "Key_Col": 2, "Child": "e", "Parent": "d" }
{ "Key_Col": 2, "Child": "d", "Parent": "c" }
{ "Key_Col": 2, "Child": "c", "Parent": "b" }
{ "Key_Col": 2, "Child": "b", "Parent": "a" }
{ "Key_Col": 2, "Child": "a", "Parent": "null" }

(등.)

답변2

사용행복하다(이전 Perl_6)

~$ raku -e 'my @a; for lines() {.split("|") andthen @a.push: .[0] => ("null".Slip, .[1].split(",").Slip) };  \
            put .invert.invert.join("\n") for [Z=>] @a.map(*.key), @a.map(*.value.rotor(2 => -1) );'   file

위 내용은 Perl 계열의 프로그래밍 언어인 Raku로 작성된 답변입니다. 즉, 첫 번째 문에서 배열이 선언됩니다. 두 번째 명령문에서는 lines()먼저 bar에 키를 가져온 다음 값을 쉼표로 나누어 "해시 배열"을 읽습니다. A는 각 값 계열의 시작 부분에 추가됩니다. Raku는 (고맙게도) 배열 요소를 자동으로 평면화하지 않기 때문에 를 호출하여 평면화를 수행합니다. 마지막 문에서는 "빈"으로 채워진 값이 겹쳐서 채워지고 각 행의 키와 값이 쌍으로 압축됩니다. 키를 연관된 각 값으로 확장하기 위해 두 번 편집한 다음 또는로 인쇄합니다.split|,"null".Sliprotor[Z=>]invertsayput

입력 예:

1|a,b,c,d
2|a,b,c,d,e
3|a,b,c

출력 예(비역전 열):

1   null a
1   a b
1   b c
1   c d
2   null a
2   a b
2   b c
2   c d
2   d e
3   null a
3   a b
3   b c

.skip헤더 행을 처리해야 하는 경우 간단한 방법은 after 호출 lines(기본적으로 행 건너뛰기)을 추가한 다음 선택한 헤더를 다시 추가하는 것입니다 say().

마지막으로 OP의 요청은 실제로 열의 순서를 바꾸는 것인데, 이는 마지막 문에 호출을 삽입하고 .reverseas에 대한 최종 준비를 작성하여 수행할 수 있습니다.@a.map(*.value.rotor(2 => -1)@a.map(*.value.reverse.rotor(2 => -1)

출력 예(역방향 열):

1   d c
1   c b
1   b a
1   a null
2   e d
2   d c
2   c b
2   b a
2   a null
3   c b
3   b a
3   a null

https://raku.org

관련 정보