두 개의 텍스트 파일이 있습니다. 첫 번째 파일은 다음과 같이 탭으로 구분된 파일입니다.
chrom pos ref alt a1 a2 a3 a4
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
두 번째 파일은 다음과 같습니다.
a1
a4
두 번째 파일에 있는 첫 번째 파일의 열과 첫 번째 파일의 처음 4개 열을 추출하고 싶습니다. 따라서 위의 경우 출력은 다음과 같습니다.
chrom pos ref alt a1 a4
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
쉘에서 이 작업을 수행하고 싶습니다. 어떻게 해야 하나요? 내 파일이 여기에 표시된 것보다 크므로 첫 번째 파일에 많은 열이 있습니다.
cut -f 1-4,$(grep -Fwf file2.txt <(head -1 file1.txt)) file1.txt
답변1
perl
문제가 없는 경우 :
$ perl -F'\t' -lane 'if(!$#ARGV){ $h{$_}=1; close ARGV if eof; next }
@i = grep { exists $h{$F[$_]} } 4..$#F if $.==1;
print join "\t", @F[0..3, @i]' f2.txt f1.tsv
chrom pos ref alt a1 a4
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
해시 변수는 두 번째 파일의 행을 키로 사용합니다.
그런 다음 grep
해시 키에 대해 필드 이름을 테스트하여 TSV 파일의 헤더 행에서 인덱스를 가져오는 데 사용됩니다.
마지막으로 처음 4개 열과 필터링된 인덱스 값이 인쇄에 사용됩니다.
답변2
표준 명령을 사용하여 두 번째 파일(여기서 호출) paste
에 쉼표로 구분된 필드 이름 목록을 만들 수 있습니다.file2
$ paste -s -d , - <file2
a1,a4
우리는 그것을 사용할 수 있습니다밀러( mlr
), 구조화된 문서(예: TSV 파일)를 처리하기 위한 유틸리티 및 cut
첫 번째 파일(여기서 호출됨)에서 처음 4개 필드와 다음의 필드를 추출하는 하위 명령입니다.file2
file1
$ mlr --tsv cut -f "chrom,pos,ref,alt,$(paste -s -d , - <file2)" file1
chrom pos ref alt a1 a4
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
Miller의 하위 명령을 사용하면 cut
필드 이름을 사용하여 추출할 필드를 선택할 수 있습니다. 여기서는 파일의 이름뿐만 아니라 이름별로 처음 4개의 필드를 선택합니다 file2
.
이 cut
하위 명령은 쉼표로 끝나는 필드 이름 목록도 허용하므로 이전에 언급한 명령 tr '\n' ',' <file2
대신 이를 사용하도록 선택할 수 있습니다.paste
file3
예를 들어 다음과 같은 파일에서 유지하려는 필드 인덱스를 얻은 경우 :
5
8
cut
... 다음과 같은 표준 명령을 사용하여 필요한 데이터를 추출 할 수 있습니다 .
$ cut -f "1-4,$(paste -s -d , - <file3)" file1
chrom pos ref alt a1 a4
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
다소 복잡한 파이프라인을 사용하여 원본 파일에서 숫자 필드 인덱스 목록을 만들 수 있습니다.
$ head -n 1 file1 | tr '\t' '\n' | grep -xF -f file2 -n | cut -d : -f 1 | paste -s -d , -
5,8
이렇게 하면 데이터에서 헤더 행이 추출되고 탭 문자가 줄 바꿈으로 바뀌어 각 필드 이름이 별도의 줄에 표시됩니다. 그런 다음 grep
두 번째 파일의 필드 이름에 해당하는 줄 번호를 출력합니다. 이는 양식으로 출력됩니다 5:a1
. 여기서 그 앞의 숫자 :
는 줄 번호이고 끝에 있는 텍스트는 일치하는 필드 이름입니다.
해당 숫자를 분리하는 데 사용 cut
하고 paste
명령을 사용하여 모든 필드 색인을 쉼표로 구분된 목록에 넣습니다.
따라서 이 답변의 상단에 있는 명령의 기능을 에뮬레이트하는 전체 명령은 mlr
다음과 같습니다.
cut -f "1-4,$(
head -n 1 file1 | tr '\t' '\n' |
grep -xF -f file2 -n | cut -d : -f 1 |
paste -s -d , -
)" file1
답변3
awk를 사용하십시오.
$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR == FNR {
a[$1]
next
}
FNR == 1 {
for ( inFldNr=1; inFldNr<=NF; inFldNr++ ) {
if ( (inFldNr <= 4) || ($inFldNr in a) ) {
out2in[++numOutFlds] = inFldNr
}
}
}
{
for ( outFldNr=1; outFldNr<=numOutFlds; outFldNr++ ) {
inFldNr = out2in[outFldNr]
printf "%s%s", $inFldNr, (outFldNr<numOutFlds ? OFS : ORS)
}
}
$ awk -f tst.awk file2 file1
chrom pos ref alt a1 a4
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
효율성을 위해 file1
위의 각 줄은 인쇄하려는 출력 필드 수만큼만 읽혀지므로 입력에 1000개의 필드가 있지만 10개의 필드만 인쇄하려는 경우 각 줄은 그 이상이 아니라 10번 반복됩니다. 1,000개 이상.
답변4
사용행복하다(이전 Perl_6)
~$ raku -e 'my ($header,@a) = lines.map: *.split(/ \s+ /);
$header .= list; my @ind = <a1 a4>;
my @col = (0...3, $header.grep( / @ind /, :k ).Slip);
put $header[@col].join("\t");
say $_.join("\t") for @a.map: *.[@col];' data.csv
위 내용은 Perl 계열의 프로그래밍 언어인 Raku로 작성된 답변입니다. Perl과 마찬가지로 Raku는 생물정보학과 같은 분야의 텍스트 구성에 매우 적합합니다.
먼저 Raku의 루틴을 사용하여 데이터를 읽습니다 lines
. 이 줄은 split
빈 공간에 있습니다. 데이터를 $header
스칼라 및 @a
배열에 저장하면 Raku는 해당 데이터가 $header
첫 번째 행을 차지하고 다음 명령문이 경량에서 으로 $header
업그레이드된다는 것을 알고 있습니다 . 이제 필요한 열 이름을 인라인으로 가져와 배열에 저장합니다.Seq
list
@ind
grep
필요한 열 이름의 헤더를 제공하여 해당 인덱스 위치(값 대신)를 반환 합니다 :k
. 그런 다음 @col
필요한 처음 4개 열과 그 결과를 사용하여 배열을 만듭니다. 0...3
이 grep
결과 Slip
는 결합되어(즉, 평면화되어) 간단한 인덱스를 만듭니다.
@array[@index]
행을 처리하는 경우 관용구(한 줄 헤더의 경우)를 사용합니다 . 우리는 열을 다루고 있으므로 map
배열 요소(예: )로 들어가야 합니다 @a.map: *.[@col]
. 마지막으로 데이터가 join
탭에 다시 표시되고 출력됩니다 put
.
입력 예:
chrom pos ref alt a1 a2 a3 a4
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
예제 출력:
chrom pos ref alt a1 a4
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
두 파일을 모두 처리하도록 코드를 업그레이드하는 것은 간단합니다. 간단히 경로를 사용하여 @ind
인덱스를 생성하면 열 인덱스 파일을 데이터 파일에서 개념적으로 분리하는 데 도움이 됩니다.
my @ind = "/path/to/index.csv".IO.lines;
명령줄에서 데이터를 가져오는 것을 선호하는 경우 Raku는 @*ARGS
이를 위한 배열을 제공합니다. Raku의 IO
루틴을 사용하여 파일이 원하는 순서대로 입력되는지 확인하세요.
~$ raku -e 'my ($header,@a) = @*ARGS[0].IO.lines.map: *.split(/ \s+ /);
$header .= list; my @ind = @*ARGS[1].IO.lines;
my @col = (0...3, $header.grep( / @ind /, :k ).Slip);
put $header[@col].join("\t");
say $_.join("\t") for @a.map: *.[@col];' data.csv index.csv
chrom pos ref alt a1 a4
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd