일부 셀에 여러 값이 포함된 대용량 CSV 파일이 있습니다. 최소값만 포함하도록 이러한 셀을 어떻게 변경할 수 있습니까?
예를 들어, 다음 입력이 주어지면:
id,disease_1,disease_2
1001,2008;2009,2009;2010
노트
- 열/필드 구분 기호는 쉼표입니다.
,
- 각 셀의 값은 세미콜론으로 구분되어
;
오름차순으로 정렬됩니다. - 열 2에서 알고리즘을 시작하고 싶습니다.
원하는 출력:
id,disease_1,disease_2
1001,2008,2009
답변1
하위 명령과 함께 put
사용되는 "표현식" 은 다음과 같습니다.put
밀러( mlr
; 구조화된 데이터 작업을 위해 특별히 설계된 도구)는 ;
각 비필드에서 분리된 값의 최소값을 계산합니다 id
.
for (key,value in mapexcept($*, "id")) {
value !=~ ";" { continue }
var minimum = "";
for (i,number in splitnv(value, ";")) {
minimum = min(minimum, number)
}
$[key] = minimum;
}
여기에서는 각 레코드의 필드를 반복하지만 호출되면 필드를 건너뜁니다 id
. 필드 값을 세미콜론으로 분할하여 생성된 숫자를 반복하면서 각 필드의 최소값을 추적합니다. 루프가 끝나면 필드 값이 발견된 최소값으로 덮어쓰여집니다. 포함되지 않은 필드는 ;
루프 시작 시 건너뜁니다.
다음을 사용하여 실행할 수 있습니다.
mlr --csv put -e script file.csv
... script
위의 짧은 스크립트를 저장할 파일 이름은 어디에 있습니까? 아니면 다음과 같이 명령줄에 철자를 입력할 수 있습니다.
mlr --csv put 'for (k,v in mapexcept($*,"id")) { v !=~ ";" { continue } var m=""; for (i,n in splitnv(v,";")) { m=min(m,n) } $[k]=m; }' file.csv
질문의 데이터를 고려하여 결과는 다음과 같아야 합니다.
id,disease_1,disease_2
1001,2008,2009
최신 버전의 Miller(버전 6+)를 사용하면 코드를 단축할 수 있습니다.상당히새로운 sort()
sum get_values()
함수를 사용하면 다음과 같습니다.
mlr --csv put 'for (k,v in mapexcept($*,"id")) { $[k] = sort(get_values(splitnv(v,";")))[1] }' file.csv
각 필드에 대해 분리된 값 목록에서 첫 번째 값을 선택합니다 ;
.
(감사해요강철 드라이버이 영리한 재작성을 깨닫게 해주었습니다. )
값이 이미 정렬되어 있으면 훨씬 간단하고 효율적입니다.
mlr --csv put 'for (k,v in mapexcept($*,"id")) { $[k] = sub(v,";.*","") }' file.csv
;
이렇게 하면 각 필드 의 첫 번째 문자가 잘립니다.
답변2
답변3
간단한 CSV인 경우:
$ perl -MList::Util=min -F, -le 'print join ",", shift@F, map {min split /;/} @F' file.csv
id,disease_1,disease_2
1001,2008,2009
답변4
awk의 경우 어린이가 아직 오름차순으로 정렬되지 않았다고 가정합니다.
awk '
BEGIN{ FS=OFS="," }
function min(list) {
subNums=split(list, numbr, /;/)
min=numbr[1]
for(n=2; n<=subNums; n++)
if(numbr[n]<min)
min=numbr[n]
return min
}
{
for(fldNr=2; fldNr<=NF; fldNr++)
$fldNr=min($fldNr)
}' infile.csv
GNU awk를 사용하십시오(asort()
기능배열도 정렬하십시오.PROCINFO["sorted_in"]
옵션).
awk '
BEGIN{ FS=OFS=","; PROCINFO["sorted_in"]="@val_num_asc" }
{
for(fldNr=2; fldNr<=NF; fldNr++)
{
subNums=split(fldNr, numArr, /;/)
asort(numArr)
$fldNr=numArr[1]
}
}' infile.csv
BEGIN{ FS=OFS="," }
입력 및 출력 필드 구분 기호를 ","로 설정합니다.subNums=split(fldNr, numArr, /;/)
현재 필드를;
하위 구분 기호로 분할하고 결과 값을 이름이 지정된 배열에 저장합니다.일련번호.그런 다음 우리는
asort(numArr)
정렬하다일련번호값은 오름차순으로 정렬됩니다.$fldNr=numArr[1]
정렬 후 첫 번째(최소) 값을 할당합니다 .일련번호현재 필드 #에 대한 배열입니다.
첫 번째 awk 명령은 동일한 작업을 수행하지만 for 루프를 사용하여 하위 숫자를 반복하고 GNU awk 확장을 사용하지 않고 각 숫자 쌍을 한 번에 하나씩 비교하여 최소값을 찾습니다.
그러나 각 필드의 하위 번호를 오름차순으로 정렬하면 첫 번째 하위 번호(가장 작은 숫자)만 선택하면 되기 때문에 코드가 훨씬 간단해집니다.
awk '
BEGIN{ FS=OFS="," }
{
for(fldNr=2; fldNr<=NF; fldNr++)
{
split($fldNr, numbr, /;/)
$fldNr=numbr[1]
continue
}
}' infile.csv