【问题标题】:How to print row(s) if they meet a certain range如果它们满足一定范围,如何打印行
【发布时间】:2021-07-20 15:25:49
【问题描述】:

我有两个如下所示的巨型文件:

f1:

chr1,3073253,3074322,gene_id,"ENSMUSG00000102693.1",gene_type,"TEC"
chr1,3074253,3075322,gene_id,"ENSMUSG00000102693.1",transcript_id,"ENSMUST00000193812.1"
chr1,3077253,3078322,gene_id,"ENSMUSG00000102693.1",transcript_id,"ENSMUST00000193812.1"
chr1,3102916,3103025,gene_id,"ENSMUSG00000064842.1",gene_type,"snRNA"
chr1,3105016,3106025,gene_id,"ENSMUSG00000064842.1",transcript_id,"ENSMUST00000082908.1"

f2:

chr,name,start,end
chr1,linc1320,3073300,3074300
chr3,linc2245,3077270,3078250
chr1,linc8956,4410501,4406025

我想要做的是在文件 1 的单独列中打印文件 2 的行,如果文件 2 的 startend 列的范围在文件 1 的范围内(第 2 列和第 3 列),并且chr 是一样的。所以根据我提供的虚拟示例文件 - 所需的输出应该是(只有linc1320 的范围在文件1的第一行):

chr1,3073253,3074322,gene_id,"ENSMUSG00000102693.1",gene_type,"TEC",linc1320,3073300,3074300
chr1,3074253,3075322,gene_id,"ENSMUSG00000102693.1",transcript_id,"ENSMUST00000193812.1"
chr1,3077253,3078322,gene_id,"ENSMUSG00000102693.1",transcript_id,"ENSMUST00000193812.1"
chr1,3102916,3103025,gene_id,"ENSMUSG00000064842.1",gene_type,"snRNA"
chr1,3105016,3106025,gene_id,"ENSMUSG00000064842.1",transcript_id,"ENSMUST00000082908.1"

我不是专业的编码员,但我一直在使用此代码根据 file2 手动更改范围:

awk -F ',' '$2<=3073300,$3>=3074300, {print $1,$2,$3,$4,$5,$6,$7}' f1.csv

我对使用特定的编程语言没有特别的偏好 - Pythonawk 都会很有帮助。感谢您提供任何帮助。

【问题讨论】:

  • 您可以在 python 中使用 pandas 库还是希望在核心 python 中使用脚本?
  • 我也想有 pandas 的解决方案 - 谢谢。

标签: python pandas awk


【解决方案1】:

编辑: 使用 OP 的编辑输入,可以尝试跟随。即使文件 2 中的字段超过 4 个,这也可以工作。

awk '
BEGIN{
  FS=OFS=","
}
FNR==NR{
  start[++count]=$2
  end[count]=$3
  match($0,/,.*/)
  val[count]=substr($0,RSTART-1,RLENGTH-1)
  next
}
{
  for(i=1;i<=count;i++){
    if(start[i]>$2 && end[i]<$3){
      print $0 OFS val[i]
      next
    }
  }
}
1' file2 file1


对于您显示的示例,您能否尝试以下操作。在 GNU awk 中编写和测试,应该可以在任何 awk 中工作。参考 anubhava 的回答。

awk '
BEGIN{
  FS=OFS=","
}
FNR==NR{
  start[++count]=$2
  end[count]=$3
  val[count]=$0
  next
}
{
  for(i=1;i<=count;i++){
    if(start[i]>$2 && end[i]<$3){
      print $0 OFS val[i]
      next
    }
  }
}
1' file2 file1

说明:为上述添加详细说明。

awk '                                  ##Starting awk program from here.
BEGIN{                                 ##Starting BEGIN section of this program from here.
  FS=OFS=","                           ##Setting FS and OFS as comma here. 
}
FNR==NR{                               ##Checking condition which will be true when file2 is being read.
  start[++count]=$2                    ##Creating start array with count variable as as index and has $2 value in it.
  end[count]=$3                        ##Creating end array with count as index and value is $3.
  val[count]=$0                        ##Creating val array with index of count and value as $0.
  next                                 ##next will skip all further statements from here.
}
{
  for(i=1;i<=count;i++){               ##Running for loop till value of count here.
    if(start[i]>$2 && end[i]<$3){      ##Checking condition if start[i]>$2 AND end[i]<$3.
      print $0 OFS val[i]              ##Then printing current line with OFS, val here.
      next                             ##next will skip all further statements from here.
    }
  }
}
1                                      ##1 will print current line here.
' file2 file1                          ##Mentioning Input_file names here.

【讨论】:

  • 感谢您的输入-使用您的代码,我只得到 f1 的第 1 和第 4 行,但实际上应该打印 file1 的唯一第 1 和第 3 行,因为 file2 中的范围位于 file1 的第 1 行和第 3 行范围内。
  • 如果您的代码也可以将文件 2 的第一列(名称)的名称作为单独的列写入输出,那就太好了。
  • @Apex,如果您想逐行比较两个文件,能否请您确认一次?(我的意思是行号?或者 file2 只有 3 行应该作为整个 file1 的索引? ) 请确认一次。
  • 它不应该是逐行比较 - 如果 f2 中的范围落在 f1 中的范围内,则应该打印 f1 的行(``` 和来自 f2 的行的名称为输出中的单独列```)
【解决方案2】:

你可以使用这个awk:

awk 'BEGIN{FS=OFS=","} FNR==NR {if (FNR>1) {chr[++n] = $1; id[n]=$2; r1[n]=$3; r2[n]=$4}; next} {for (i=1; i<=n; ++i) if ($1 == chr[i] && r1[i] > $2 && r2[i] < $3) {$0 = $0 OFS id[i] OFS r1[i] OFS r2[i]; break}} 1' file2 file1

chr1,3073253,3074322,gene_id,"ENSMUSG00000102693.1",gene_type,"TEC",linc1320,3073300,3074300
chr1,3074253,3075322,gene_id,"ENSMUSG00000102693.1",transcript_id,"ENSMUST00000193812.1"
chr1,3077253,3078322,gene_id,"ENSMUSG00000102693.1",transcript_id,"ENSMUST00000193812.1"
chr1,3102916,3103025,gene_id,"ENSMUSG00000064842.1",gene_type,"snRNA"
chr1,3105016,3106025,gene_id,"ENSMUSG00000064842.1",transcript_id,"ENSMUST00000082908.1"

更易读的形式:

awk '
BEGIN { FS = OFS = "," }
FNR == NR {
   if (FNR > 1) {
      chr[++n] = $1
      id[n] = $2
      r1[n] = $3
      r2[n] = $4
   }
   next
}
{
   for (i=1; i<=n; ++i)
      if ($1 == chr[i] && r1[i] > $2 && r2[i] < $3) {
         $0 = $0 OFS id[i] OFS r1[i] OFS r2[i]
         break
      }
} 1' file2 file1

【讨论】:

  • 谢谢 - 您的代码运行良好 - 不幸的是,我忘记了我的问题中非常重要的部分(现已更新) - 问题中的一切都保持不变,但我添加了一个名为 chr 的列在file2中 - 所以代码应该像现在一样运行,但在开始查看范围之前,代码应该查看文件中的chr,如果chr相同并且范围在file1内,那么应该打印输出。所以现在所需的输出应该是我上面更新的。如果您能帮我解决这个问题,那就太好了 - 很抱歉给您带来不便。
  • 确定检查更新的答案。顺便说一句,您的示例代码中有ch1 而不是chr1,我认为这是一个错字。
  • 非常感谢您的帮助 - 我真的很感激。确实是笔误,我更正了。
【解决方案3】:

让我们尝试以pandas的方式解决问题,首先将csv文件读入pandas数据帧

f1 = pd.read_csv('file1.csv', header=None)
f2 = pd.read_csv('file2.csv')

>>> f1

      0        1        2        3                     4              5                     6
0  chr1  3073253  3074322  gene_id  ENSMUSG00000102693.1      gene_type                   TEC
1  chr1  3074253  3075322  gene_id  ENSMUSG00000102693.1  transcript_id  ENSMUST00000193812.1
2  chr1  3077253  3078322  gene_id  ENSMUSG00000102693.1  transcript_id  ENSMUST00000193812.1
3  chr1  3102916  3103025  gene_id  ENSMUSG00000064842.1      gene_type                 snRNA
4  chr1  3105016  3106025  gene_id  ENSMUSG00000064842.1  transcript_id  ENSMUST00000082908.1


>>> f2

    chr      name    start      end
0  chr1  linc1320  3073300  3074300
1  chr3  linc2245  3077270  3078250
2  chr1  linc8956  4410501  4406025

现在我们可以mergefilter 满足给定区间包含条件的行,然后我们可以join 使用文件f1 过滤的行

m = f1.reset_index()\
      .merge(f2, left_on=0, right_on='chr')\
      .where(lambda x: x[1].le(x['start']) & x[2].ge(x['end']))\
      .set_index('index')[['name', 'start', 'end']]

f3 = f1.join(m)

>>> f3

      0        1        2        3                     4              5                     6      name      start        end
0  chr1  3073253  3074322  gene_id  ENSMUSG00000102693.1      gene_type                   TEC  linc1320  3073300.0  3074300.0
1  chr1  3074253  3075322  gene_id  ENSMUSG00000102693.1  transcript_id  ENSMUST00000193812.1       NaN        NaN        NaN
2  chr1  3077253  3078322  gene_id  ENSMUSG00000102693.1  transcript_id  ENSMUST00000193812.1       NaN        NaN        NaN
3  chr1  3102916  3103025  gene_id  ENSMUSG00000064842.1      gene_type                 snRNA       NaN        NaN        NaN
4  chr1  3105016  3106025  gene_id  ENSMUSG00000064842.1  transcript_id  ENSMUST00000082908.1       NaN        NaN        NaN

PS:您还可以使用 f3.to_csv('file3.csv') 将生成的数据帧 f3 保存到 csv 文件

【讨论】:

    猜你喜欢
    • 2015-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多