두 번째 열 값을 기준으로 CSV 파일 분할

두 번째 열 값을 기준으로 CSV 파일 분할

저는 Ubuntu를 사용하고 있으며 두 번째 열(연령)의 값을 기준으로 csv 파일을 두 개의 csv 파일로 분할하고 싶습니다. 첫 번째 파일은 60세 미만(<60) 환자용이고 두 번째 파일은 60세 이상(>=) 환자용입니다. 예를 들어, 다음과 같은 입력이 있는 경우:

id,age
1,65
2,63
3,5
4,55
5,78

원하는 출력은 다음과 같습니다.

파일 아래:

id,age
3,5
4,55

파일 범위:

id,age
1,65
2,63
5,78

다음 코드를 시도했지만 헤더(열 이름)가 제거되었습니다. 이를 방지하려면 어떻게 해야 합니까?

awk -F ',' '($2>=60){print}' file.csv > file_over.csv 

입력 파일에는 약 50,000개의 라인이 있습니다.

답변1

나는 내 데이터를 단지 문자 행이 아니라 테이블의 내용으로 이미 이해하고 있는 환경에서 이 모든 필터링을 수행할 것입니다.

필터링을 수행하는 것 같으므로 이상적으로 환경에는 테이블을 쿼리하는 데 사용할 수 있는 일종의 구조화된 언어가 있어야 합니다.

SQL을 입력하고,에스구조화된유리환자 데이터베이스 등을 처리하기 위해 고안된 언어

데이터베이스가 디스크에 존재하지 않아도 데이터베이스에 이러한 SQL 인터페이스를 제공하는 도구가 있습니다.SQLite. (대부분 우분투에 이미 설치되어 있을 가능성이 높습니다. 그렇지 않으면 매우 작으며 를 사용하여 설치할 수 있습니다 sudo apt install sqlite3.)

그럼, 보자.

  1. 우리는 귀하의 피드백을 받습니다allpeople.csv
  2. 실행하면 sqlite3 people.sqlite다음 명령을 작성할 수 있는 깔끔한 작은 셸이 제공됩니다.
  3. CREATE TABLE "people" ("id" INTEGER UNIQUE, "age" INTEGER);Enter, 두 개의 열 "id"와 "age"가 포함된 새 테이블을 생성합니다. 두 열은 모두 정수입니다. "id"는 고유함도 보장됩니다. 동일한 "id"를 가진 두 개의 항목을 가지려고 하면 불만 사항이 발생합니다.
  4. .import --csv "allpeople.csv" "people"Enter, CSV "allpeople"을 읽고 방금 생성한 "people" 테이블에 로드합니다.

이제 데이터가 준비되었습니다.(데이터베이스에서 몇 번을 선택하더라도 한 번만 수행하면 됩니다.)
여기서부터 재미가 시작됩니다.

  1. .mode csvEnter, 출력 모드를 CSV로 설정합니다.
  2. .output oldpeople.csvEnter, 이는 sqlite에게 "oldpeople.csv" 파일(요청한 헤더 포함)에 출력을 쓰도록 지시합니다.
  3. SELECT * FROM "people" WHERE "age" >= 60;Enter, 짐작하셨겠지만 60세 이상의 사람이 포함된 모든 행을 선택하고 결과를oldpeople.csv
  4. .output youngpeople.csvEnterSELECT * FROM "people" WHERE "age" < 60;Enter더 이상의 설명은 필요하지 않습니다
  5. .quitEnterSQLite를 종료합니다.

물론 위의 명령을 텍스트 파일 "commands.sql"에 작성하고 sqlite3 people.sqlite < commands.sql.

"people.sqlite"에는 이제 "allpeople.csv"에 있는 데이터베이스보다 더 읽기 쉽고, 더 컴팩트하며, 더 유연한 데이터베이스가 포함되어 있습니다. 나대개~을 피하다어느CSV에 대한 통계, 수학 또는 분석 작업 – IMHO 이는 올바른 형식이 아닙니다. SQL은 매우 편리하며 특히 여러 테이블이나 2개 이상의 열이 있는 경우 더욱 흥미로운 작업을 수행할 수 있습니다.

예를 들어, 데이터에 "성별"에 대한 또 다른 열과 "체중"에 대한 열이 하나 있는 경우 SQL은 SELECT하나의 명확한 명령문에서 18세에서 20세 사이의 심각한 과체중 남성을 모두 쉽게 선택할 수 있습니다. people 테이블의 "id"에 진단을 매핑하는 다른 테이블이 있는 경우 심각한 당뇨병을 앓고 있는 18~20세 남성을 구체적으로 찾을 수도 있습니다. (당신은 할 수아마도awk에서 동일한 작업을 수행하면 어떤 시점에서는 번거롭고 느려집니다. )

저는 실제로 데이터 교환 형식으로 잘 작동하기 때문에 SQLite를 좋아합니다. CSV와 달리 인코딩, 구분, 인용, 이스케이프, 공백 및 헤더에 동의하는 도구는 거의 없습니다. SQLite는 실제로 정의된 저장 형식이고, CSV는 대략적인 개념에 가깝습니다. 일반적으로 쉼표로 구분하면 문제가 없습니다.

데이터 분석에 사용되는 일반적인 프로그래밍 언어에는 일반적으로 sqlite3 인터페이스가 내장되어 있습니다. python3의 표준 라이브러리에는 이 sqlite3모듈이 포함되어 있고, Perl에는 이 모듈이 있고 DBD::SQLite, R에는 있고 library(RSQLite), C/C++에는 기본 인터페이스가 있습니다...

답변2

awk를 사용하는 방식은 기본적으로파일에 필드에 인용된 쉼표와 같은 고급 CSV 기능이 포함되어 있지 않은 경우. 1 값이 문자열 1 로 구문 분석되더라도 비교가 어휘가 아닌 숫자인지 확인하려면 테스트를 $2+0<60and 로 변경해야 합니다 .$2+0>=60$2

두 경우 모두 헤더 행을 내보내려면 첫 번째 레코드에 대해 true를 반환하는 테스트를 추가해야 합니다. {print}이것이 기본 작업이므로 이 컨텍스트에서는 완전히 생략 할 수 있습니다 . 그래서

$ awk -F ',' 'NR==1 || $2+0<60' file.csv
id,age
3,5
4,55

그리고

$ awk -F ',' 'NR==1 || $2+0>=60' file.csv
id,age
1,65
2,63
5,78

파일이 간단한 CSV 표준을 따르지 않는 경우 csvsqlPython 기반 csvkit에서 다른 옵션을 사용할 수 있습니다.

$ csvsql --query 'SELECT * FROM file WHERE age >= 60' file.csv
id,age
1,65
2,63
5,78

또는밀러:

$ mlr --csv filter '$age >= 60' file.csv
id,age
1,65
2,63
5,78

csvkit두 패키지 모두 millerUbuntu에서 쉽게 사용할 수 있습니다.우주저장소.


  1. 예 를 들어 , C 로케일에서는 사전순으로 다음 a보다 큽니다.6($2>=60){print}($2<60){print}

답변3

사용할 awk내장 출력리디렉션한 번에 파일 분할:

$ awk -F, -v over=file_over.csv \
          -v under=file_under.csv \
    'NR==1 { print > over; print > under ; next };
    $2 < 60 { print > under ; next };
    { print > over }' file.csv

리디렉션은 awk셸의 리디렉션과 유사하게 작동합니다. 주요 차이점은 awk는 >스크립트가 처음 작성될 때만 출력 파일을 잘라낸다는 것입니다(따라서 >>스크립트를 기존 파일에 추가하지 않는 한 후속 출력 줄은 필요하지 않습니다). .

위의 코드 줄을 사용하면 awk 변수 overunder. 일반적인 오류 원인이므로 자주 재사용되는 값에는 변수나 상수를 사용하는 것이 일반적으로 더 좋습니다.

$ awk -F, 'NR==1 {
    print > "file_over.csv";
    print > "file_under.csv" ;
    next
  };
  $2 < 60 { print > "file_under.csv" ; next };
  { print > "file_over.csv" }' file.csv```

또는 BEGIN 블록에 설정합니다.

$ awk -F, '
    BEGIN {
      over  = "file_over.csv";
      under = "file_under.csv";
    };
    NR==1 { print > over; print > under ; next };
    $2 < 60 { print > under ; next };
    { print > over }' file.csv

입력 및 출력 파일:

$ head file*.csv
==> file.csv <==
id,age
1,65
2,63
3,5
4,55
5,78

==> file_over.csv <==
id,age
1,65
2,63
5,78

==> file_under.csv <==
id,age
3,5
4,55

답변4

awk를 사용하십시오.

$ awk -F',' '
    NR==1 { print > "file_under"; print > "file_over"; next }
    { print > ( "file_" ($2 < 60 ? "under" : "over") ) }
' file

$ head file_under file_over
==> file_under <==
id,age
3,5
4,55

==> file_over <==
id,age
1,65
2,63
5,78

또는 원하는 경우 코드에서 출력 파일 이름을 반복하지 않고도 동일한 출력을 생성합니다.

awk -F',' '
    BEGIN { split("file_over,file_under",out) }
    NR==1 { for (i in out) print > out[i]; next }
    { print > out[($2 < 60)+1] }
' file

관련 정보