CSV 파일의 ";"로 구분된 값 목록의 최소값

CSV 파일의 ";"로 구분된 값 목록의 최소값

일부 셀에 여러 값이 포함된 대용량 CSV 파일이 있습니다. 최소값만 포함하도록 이러한 셀을 어떻게 변경할 수 있습니까?

예를 들어, 다음 입력이 주어지면:

id,disease_1,disease_2
1001,2008;2009,2009;2010 

노트

  1. 열/필드 구분 기호는 쉼표입니다.,
  2. 각 셀의 값은 세미콜론으로 구분되어 ;오름차순으로 정렬됩니다.
  3. 열 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

설명하다세미콜론으로 구분된 항목은 항상 오름차순으로 정렬되므로 각 그룹의 첫 번째 항목은 원하는 가장 작은 값 항목입니다. 이를 기반으로 나머지 값을 간단히 제거할 수 있습니다.

sed 's/;[^,]*//g' {file}

이는 데이터가 간단한 CSV 형식이고 세미콜론과 쉼표도 포함된 인용 텍스트 문자열을 사용하지 않는다고 가정합니다. 이 경우 이 텍스트 기반 솔루션은 작동하지 않으며 보다 완전한 솔루션을 사용해야 합니다.답변사용밀러.

샘플 데이터세트의 출력

id,disease_1,disease_2
1001,2008,2009

답변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

관련 정보