큰 csv 파일(약 1000개 열)이 있고 헤더 이름에 "만성"이라는 단어가 포함된 열만 새 파일로 추출하고 싶습니다. 어떻게 해야 하나요?
예를 들어 다음과 같은 경우가 있습니다.
gender,chronic_disease1,chronic_disease2
male,2008,2009
원하는 출력은 다음과 같습니다.
chronic_disease1,chronic_disease2
2008,2009
참고: 열/필드 구분 기호는 쉼표 ","입니다. 일치하는 항목 이 없으면 chronic
출력이 전혀 없습니다.
답변1
사용밀러(Ubuntu "유니버스" 저장소에서 사용 가능) 해당 cut
동사는 선택적으로 정규식을 사용하여 필드 이름을 일치시킬 수 있습니다.
mlr --csv cut -r -f 'chronic' file.csv
chronic
(필드 이름의 하위 문자열과 일치) 또는 더 구체적으로
mlr --csv cut -r -f '^chronic_' file.csv
(하위 문자열을 이름 시작 부분에 고정하고 뒤에 밑줄을 추가합니다) 또는
mlr --csv cut -r -f '"^chronic_"i' file.csv
후자를 대소문자를 구분하지 않고 일치시킵니다.
일치를 되돌리려면 모든 열을 선택하세요.아니요일치 ^chronic_
, 추가 -x
:
mlr --csv cut -x -r -f '"^chronic_"i' file.csv
--csvlite
참고: 입력 파일에 고급 CSV 기능(예: RFC-4180 스타일 큰따옴표)이 포함되어 있지 않은 경우 보다 효율적인 엔진을 사용할 수 있습니다. 바라보다파일 형식 - CSV/TSV/ASV/USV/등.
문자열을 포함하는 필드 이름이 없고 chronic
빈 레코드 대신 출력을 전혀 원하지 않는 경우 skip-trivial-records
추출된 데이터를 Miller의 하위 명령을 통해 전달하십시오.
mlr --csv cut -r -f 'chronic' then skip-trivial-records file.csv
답변2
awk를 사용하세요:
awk '
BEGIN{ FS=OFS="," }
NR==1{
for(i=1; i<=NF; i++)
found+=col[i]=($i ~ /chronic/)
if(!found) exit
}
{
for(i=1; i<=NF; i++)
printf ("%s", (col[i]? (c++?OFS:"")$i :"") )
printf("%s", (c?"\n" : "") ); c=0
}' infile.csv
필드 구분 기호와 출력 필드 구분 기호를 쉼표로 설정하여 입력 파일이 CSV 파일임을 나타냅니다.
첫 번째 입력 행(헤더 행이라고 가정)의 경우 col[]
해당 행의 각 필드에 하위 문자열 "이 포함되어 있는지 여부를 저장하는 배열을 만듭니다.만성병 환자" 그런 다음 TRUE/1( /chronic/
각 필드를 정규식과 일치) 또는 FALSE/0(일치하지 않는 경우).
if(!found) exit
코드의 이 부분은 출력할 필드가 없는 경우 명령을 종료하고 입력 파일 처리를 중지하도록 awk에 지시합니다. 그렇지 않으면...
...그런 다음 각 후속 행(및 첫 번째 행)에 대해 해당 행의 각 필드를 반복하고 해당 col[i]
값이 1이면 해당 필드를 인쇄하고, 그렇지 않으면 행을 처리한 후 빈 문자열을 인쇄합니다. 필드 출력이 있는 경우( c
카운터가 0이 아닐 때, c
출력이 첫 번째 필드가 아닐 때 필드 사이에 OFS를 추가하는 데에도 카운터가 사용됨) 개행을 인쇄하고, 그렇지 않으면 아무것도 인쇄하지 않고 c
0으로 재설정합니다.
답변3
필드 이름이 다음과 같이 .csv 파일의 첫 번째 줄에 있다고 가정합니다.
$ cat input.csv
gender,chronic_disease1,chronic_disease2
male,2008,2009
다음 Perl 코드 한 줄은 필드 이름에 "chronic" 문자열이 포함된 필드를 인쇄합니다.
perl -F, -lane '
if ($. == 1) { # first line of input
# get a list of field numbers & names matching "chronic"
foreach my $f (0..$#F) {
if ($F[$f] =~ /chronic/i) { # case-insensitive
push @out, $f; # get the field numbers
push @outnames, $F[$f]; # get the names too
}
};
last unless (@out); # exit early if there's nothing to print
} else {
print join(",", @outnames) if ($. == 2); # print the header only once
print join(",", @F[@out]) # print the data
}' input.csv
예제 출력:
chronic_disease1,chronic_disease2
2008,2009
참고: 이는 간단한 쉼표로 구분된 파일에서만 작동합니다. 쉼표나 줄 바꿈이 포함된 인용 필드가 포함된 CSV 파일에서는 작동하지 않습니다. 이렇게 하려면 CSV 파서를 사용해야 합니다(예: Perl의 파서).텍스트::CSV, 또는 심지어 Perl의 것DBD::CSV모듈데이터베이스 인터페이스마치 SQL 데이터베이스인 것처럼 CSV 파일에 대해 SQL 쿼리를 수행할 수 있습니다. 또는 사용밀러
답변4
사용행복하다(이전의 Perl6)
~$ raku -MText::CSV -e ' \
#read header into @hdr array
my $csv1 = Text::CSV.new;
my $fh1 = "chronic_test.txt".IO.open;
my @hdr = $csv1.header($fh1, munge-column-names => "fc").column-names;
close $fh1;
#read full csv file into @whole array
my $csv2 = Text::CSV.new;
my $fh2 = "chronic_test.txt".IO.open;
my @whole; while $csv2.getline($fh2) -> $row {
@whole.push: $row;
}; close $fh2;
#output array that has been @whole>>.[index] filtered for desired columns
.join(",").put for @whole>>.[@hdr.grep(/chronic/, :k)];'
입력 예:
gender,chronic_disease1,chronic_disease2
male,2008,2009
예제 출력:
chronic_disease1,chronic_disease2
2008,2009
Raku는 Perl 프로그래밍 언어 계열의 언어입니다. 유니코드와 강력한 정규식 구현에 대한 고급 지원을 제공합니다.
Raku의 Text::CSV
모듈은 유효한 CSV를 구문 분석하고 유효한 CSV를 출력할 수 있습니다. 대체 열 구분 기호(예: 탭)를 허용해야 하거나 인용된 필드, 빈 필드, 삽입된 줄바꿈 및/또는 쉼표 등을 처리하는 방법이 필요한 경우 아래 Markdown 문서를 확인하세요.
위의 방법은 열 이름으로 CSV 파일을 읽고 필터링하는 매우 강력하지만 장황한 방법입니다. 즉, 헤더를 두 번 읽고 정규식을 사용하여 grep
일치하는 열을 출력합니다. 필요한 경우 열 이름 을 munge
다른 대소문자( , 등)로 전환 uc
할 수 있습니다 .lc
fc
하단의 마크다운 문서는 CSV 파일을 출력하기 위해 다음 코드를 제공합니다(필수 열만 출력하도록 수정됨).
# and write CSV file, filtered as above
my $fh_out = open "new.csv", :w;
$csv.say($fh_out, $_) for @whole>>.[@hdr.grep(/chronic/, :k)];
$fh_out.close;
더 효율적: 위의 코드는 실제로 @whole
csv 파일을 한 줄씩 메모리로 읽어옵니다. 아래 코드는 @filtered
csv 열을 메모리로 읽기만 하므로 메모리 효율성이 더 높을 것입니다.
$
참고: "승격" - 서명은 매우 중요합니다.스칼라~ 에 @
서명하다대량으로다음과 같은 객체를 사용할 때"위치 인덱스". 프로모션은 다음과 같은 형태 @($index)
이거나 더 간단 할 수 있습니다 @$index
.
~ % raku -MText::CSV -e ' \
#read header into @hdr array
my $csv1 = Text::CSV.new;
my $fh1 = "chronic_test.txt".IO.open;
my @hdr = $csv1.header($fh1, munge-column-names => "fc").column-names;
my $index = @hdr.grep(/chronic/, :k); close $fh1;
#read filtered csv file into @filtered array
my $csv2 = Text::CSV.new;
my $fh2 = "chronic_test.txt".IO.open;
my @filtered; while $csv2.getline($fh2) -> $row {
@filtered.push: $row.[@$index];
}; close $fh2;
.join(",").put for @filtered;'
https://github.com/Tux/CSV/blob/master/doc/Text-CSV.md
https://docs.raku.org
https://raku.org