모든 행이 0, NA 및 1이면 모두 0으로 바꿉니다.

모든 행이 0, NA 및 1이면 모두 0으로 바꿉니다.

내 질문은 이것입니다:

네 번째 열부터 마지막 ​​열까지 모든 행 필드 값이 맞는지 확인하고 싶습니다.오직0(형식은 0.00), 1(형식은 1.00) 또는 NA이면 0값을 로 바꿉니다 0.001.

예를 들어 다음과 같은 줄이 있습니다.

MA_10 49498 49499 NA NA NA NA 0.00 NA 1.00 NA NA NA NA 1.00 NA NA NA NA 1.00 NA NA NA NA NA NA 1.00 NA NA NA NA

예상되는 결과는 다음과 같습니다.

MA_10 49498 49499 NA NA NA NA 0.001 NA 1.00 NA NA NA NA 1.00 NA NA NA NA 1.00 NA NA NA NA NA NA 1.00 NA NA NA NA

하지만 다음과 같은 행을 건드리면 안 됩니다.

MA_10 49499 49500 NA NA NA NA 0.00 NA 0.50 NA NA NA NA 1.00 NA NA NA NA 1.00 NA NA NA NA NA NA 1.00 NA NA NA NA

NA, 0, 1, 즉 0.50과 그 값이 다르기 때문입니다.

나는 smt로 이 작업을 수행했지만 1도 대체하므로 작동하지 않습니다.

#!/bin/bash -ue
BEGIN { OFS = FS = "\t" }

NR != 1 {
    for (i = 4; i <= NF; ++i) {
        if ($i = "0" || $i= "1") {
            $i = "0.01";
        }
    }
}

{ print $0 }

미리 감사드립니다!

답변1

01.awk:

BEGIN{FS=OFS="\t"}
skip=0
{
    for(i=4;i<=NF;i++){
        if($i !~ /NA/ && $i!=0 && $i!=1){
            skip=1
            break
        }
    }
}
!skip{gsub(/0\.0+/,"0.001")}
1

for 루프는 네 번째 열에서 시작하는 행에서 NA가 아닌, 0이 아닌, 1이 아닌 필드를 찾으려고 시도합니다. 발견되면 skip1로 설정됩니다.

!skip{gsub(/0\.0+/,"0.001")}

수행되지 않으며 0.00가 되지 않습니다 0.001.

스크립트 실행

awk -f 01.awk inputfile

추신: #!/bin/bash -uebash 스크립트가 아닌 awk 스크립트를 작성했기 때문에 귀하의 시도는 실제로 의미가 없습니다.

답변2

다음을 시도해 볼 수 있습니다(더 쉽게 읽을 수 있도록 "줄 연속"을 사용하여 줄로 나누고 줄 끝에 백슬래시를 추가했습니다).

awk -F'\t' -v OFS='\t' '{delete a; nzero=0;\
      for (i=4;i<=NF;i++){\
        if ($i==0) a[++nzero]=i;\
        if ($i!=0 && $i!=1 && $i!="NA") {print; next;}\
      }\
      for (i=1;i<=nzero;i++) {$a[i]=0.001;}; print;}' input.txt
  • 그러면 모든 행을 구문 분석하여 숫자 값이 0인 필드(필드 4부터 시작)를 확인하고 필드 번호를 배열에 저장 a하고 해당 필드의 수를 에 저장합니다 nzero.
  • 또한 체크 필드가 있는지 확인합니다.아니요0, 1 또는 "NA"와 일치합니다. 그렇다면 해당 줄은 "있는 그대로" 인쇄되고 실행은 다음 줄로 이동합니다.
  • "불법" 필드가 발견되지 않으면 a배열이 구문 분석되고 거기에 저장된 모든 필드 번호가 로 대체됩니다 0.001.

delete a배열을 지우는 구문에는 GNU Awk가 필요합니다. 다른 구현의 경우 split("",a)대신 사용하세요.

Awk 스크립트와 동일합니다(라고 부르겠습니다 replace.awk):

#!/bin/awk -f
BEGIN{FS=OFS="\t"}

{
  delete a;
  nzero=0;

  for (i=4;i<=NF;i++)
  {
    if ($i==0) a[++nzero]=i;
    if ($i!=0 && $i!=1 && $i!="NA")
    {
      print;
      next;
    }
  }
  for (i=1;i<=nzero;i++) $a[i]=0.001;
  print;
}

사용

awk -f replace.awk input.txt

답변3

이는 awk정규식을 통해 수정할 레코드를 선택할 수 있는 또 다른 방법입니다.

$ awk '/^([\t]*[^\t]+){3}([\t]+([01][.]00|NA))+$/ && gsub(/0\.00/, "0.001") || 1' file

정규식 디코딩:

^([\t]*[^\t]+){3} 현재 레코드의 처음 세 필드를 반복합니다(필드는 탭으로 구분됩니다).

([\t]+([01][.]00|NA))4번째부터 시작하는 "좋은" 필드의 형태입니다.

양호 필드 뒤 +와 레코드 끝까지 를 배치하면 세 번째 필드 이후의 모든 필드가 "양호"함을 의미합니다. 이는 이것이 우리가 수정하려는 행임을 의미합니다.

gsub"0.00"을 "0.001"로 대체 합니다 .

===============================

다음은 정규식을 동적으로 생성하고 변경하는 awk 코드입니다.

$ gudFld="[01][.]00|NA" \
    awk '
       function enc(arg) {
         return "(" arg ")"
       }
       BEGIN {
         sp = "\t"
         s = "["  sp "]"
         S = "[^" sp "]"
         f_ = s"*" S"+"; f = enc(f_)
         f3 = f"{3}"
         e = enc( ENVIRON["gudFld"])
         g_ = s"+" e; g = enc(g_)
         pat = "^" f3 g"+" "$"
       }
       $0 ~ pat && gsub(/0.00/, "&1") || 1
' file

편집증을 갖고 싶고 0.00패턴이 처음 세 필드에 나타날 가능성을 고려한다면 마지막 줄을 다음으로 바꿀 수 있습니다. 여기서는 4번째 필드부터 gsub만 실행합니다.

$0 ~ pat {
   match($0, f3)
   f123 = substr($0, 1, RLENGTH)
   f4_end = substr($0, RLENGTH+1)
   gsub(/0.00/, "&1", f4_end)
   $0 = f123 f4_end
}1

관련 정보