이것은 내 입력 파일입니다.
0164318,001449,001452,001922
0164318,001456,001457,001922
0842179,002115,002118,001485
0846354,001512,001513,001590
0841422,001221,001224,001860
0841422,001227,001228,001860
나는 내 결과가
0164318,001449,001457,001922
0842179,002115,002118,001485
0846354,001512,001513,001590
0841422,001221,001228,001860
col1별로 그룹화하고 쉘 스크립트를 통해 min(col2) 및 max(col3)를 찾습니다.
답변1
사용csvkit
,
$ csvsql -H --query 'SELECT a,min(b),max(c),d FROM file GROUP BY a' file.csv
a,min(b),max(c),d
164318,1449,1457,1922
841422,1221,1228,1860
842179,2115,2118,1485
846354,1512,1513,1590
이는 CSV 데이터를 임시 데이터베이스(기본적으로 SQLite라고 생각함)에 로드한 다음 주어진 SQL 쿼리를 여기에 적용합니다. 기본적으로 테이블의 이름은 입력 파일과 동일하며(접미사 없음) 데이터에 열 헤더가 없기 때문에 기본 필드 이름은 알파벳 순서로 지정됩니다.
이 -H
옵션은 csvsql
데이터에 열 머리글이 없음을 나타냅니다.
생성된 헤더를 출력에서 제거하려면 다음과 같은 것을 전달하십시오 sed '1d'
.
0으로 채워진 정수를 얻으려면 다음을 수행하십시오.
$ csvsql -H --query 'SELECT printf("%07d,%06d,%06d,%06d",a,min(b),max(c),d) FROM file GROUP BY a' file.csv
"printf(""%07d,%06d,%06d,%06d"",a,min(b),max(c),d)"
"0164318,001449,001457,001922"
"0841422,001221,001228,001860"
"0842179,002115,002118,001485"
"0846354,001512,001513,001590"
여기서는 실제로 결과 레코드당 하나의 출력 필드만 요청하기 때문에(쉼표가 포함되어 있음) 이 줄을 인용했습니다. 더 많은 입력이 필요하지만 추가 큰따옴표를 생성하지 않는 또 다른 접근 방식은 다음과 같습니다.
$ csvsql -H --query 'SELECT printf("%07d",a),printf("%06d",min(b)),printf("%06d",max(c)),printf("%06d",d) FROM file GROUP BY a' file.csv
"printf(""%07d"",a)","printf(""%06d"",min(b))","printf(""%06d"",max(c))","printf(""%06d"",d)"
0164318,001449,001457,001922
0841422,001221,001228,001860
0842179,002115,002118,001485
0846354,001512,001513,001590
마찬가지로, 결과를 파이핑하여 출력 헤더를 제거할 수 있습니다 sed '1d'
.
답변2
사용csvkit
:
csvsql -H --query "select a,min(b),max(c),d from file group by a,d" file.csv
이렇게 하면 선행 0이 잘립니다.
산출:
a,min(b),max(c),d
164318,1449,1457,1922
841422,1221,1228,1860
842179,2115,2118,1485
846354,1512,1513,1590
답변3
밀러(http://johnkerl.org/miller/doc), 사용
mlr --ocsv --quote-all --inidx --ifs , cat inputFile | \
mlr --ocsv --quote-none --icsvlite stats1 -g '"1"' -a min,max,min -f '"2","3","4"' \
then cut -f '"1","2"_min,"3"_max,"4"_min' \
then label id,col2,col3,col4 | sed 's/"//g'
당신은
id,col2,col3,col4
0164318,001449,001457,001922
0842179,002115,002118,001485
0846354,001512,001513,001590
0841422,001221,001228,001860
답변4
SQL을 기본 절차적 작업으로 나누고 이를 쉘 스크립트에 복제할 수 있습니다.
SQL과 같은 선언적 언어의 장점 중 하나는 개발자가 절차적 구현의 장황함과 복잡성을 숨겨서 데이터에만 집중할 수 있다는 점에서 좋은 생각이 아닙니다. (최적화는 선언적 언어의 두 번째 큰 장점으로, 절차적 프로그램을 사용하여 복제하면 사라집니다.)
게다가 이 접근 방식은 문제가 있습니다.쉘 루프에서 텍스트를 처리하는 것은 일반적으로 나쁜 습관으로 간주됩니다..
그러나 다음은 많은 시스템에 사전 설치된 표준 유틸리티를 사용하는 쉘 스크립트의 예입니다(배열 구성 제외 - POSIX에 지정되지는 않았지만 요청한 이후 널리 사용 가능하고 확실히 작동함 bash
).
#!/bin/bash
# The input file will be passed as the first argument
file="$1"
# For each input line:
# We take only the values of the first field, sort them, remove duplicates
for i in $(cut -d ',' -f 1 "$file" | sort -n -u); do
# Resetting the array is not really needed; we do it for safety
out=()
# The first field of the output row is the key of the loop
out[0]="$i"
# We only consider the rows whose first field is equal
# to the current key (grep) and...
# ... we sort the values of the second field
# in ascending order and take only the first one
out[1]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 2 | sort -n | head -n 1)"
# ... we sort the values of the third field in
# ascending order and take only the last one
out[2]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 3 | sort -n | tail -n 1)"
# ... we sort the values of the fourth field in
# ascending order and take only the first one
out[3]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 4 | sort -n | head -n 1)"
# Finally we print out the output, separating fields with ','
printf '%s,%s,%s,%s\n' "${out[@]}"
done
다음과 같이 호출되어야 합니다.
./script file
이 스크립트는 다음과 같습니다.
SELECT col1, MIN(col2), MAX(col3), MIN(col4)
FROM text
GROUP BY col1
ORDER BY col1