如何将特定列与其余列分开

时间:2020-01-23 11:58:45

标签: r python-3.x shell awk statistics

我有这样的矩阵(其余的第一列名称是值,分隔符i标签):

name1 A1 B1 C1 D1
name2 A2 B2 C2 D2

矩阵可能很大(平均约有数百行和列)。大小相同。我可以期待零值。

我需要这样的输出:

name1 A1 B1 C1 D1 A1/B1 A1/C1 A1/D1
name2 A2 B2 C2 D2 A2/B2 A2/C2 A2/D2

此组合保存到新文件。然后进行另一组合:

name1 A1 B1 C1 D1 B1/A1 B1/C1 B1/D1
name2 A2 B2 C2 D2 B2/A2 B2/C2 B2/D2

,依此类推=>将矩阵中的每一列与其余列分开,并保存为TSV到新文件中。并四舍五入到小数点后三位。

我可以使用脚本手动完成此操作

awk '{OFS="\t"}{$6=$2/($3+0.001); $7=$2/($4+0.001); $8=$2/($5+0.001)}1' input_file.tsv

为什么我加0.001的原因是不可能除以零。我可以使用wile循环创建shell脚本,但是需要很长时间。

对于此过程的任何自动化,我将感到非常高兴。

2 个答案:

答案 0 :(得分:5)

能否请您尝试以下。同样通过查看您的尝试,我假设您的Input_file由空格而不是逗号分隔,如果除空格之外还有其他分隔符,则在以下代码中也添加BEGIN{FS=","}(以逗号为例)。感谢@accdias添加了逻辑来也删除了控制M个字符

awk '
{
   gsub(/\r/,"")
}
{
  nf=NF
  close(out_file)
  for(k=2;k<=nf;k++){
    out_file=""
    for(i=2;i<=nf;i++){
      if($i!=0){
         $(NF+1)=sprintf("%.03f",$k/$i)
      }
      else{
         $(NF+1)=sprintf("%s","NaN")
      }
    }
    out_file=k"field_out_file"
    print >> (out_file)
    NF=nf
  }
}'  Input_file

代码处理的是什么:

  • 它将根据字段名称创建输出文件名,例如2field_out_file,这意味着第二个字段将被整个Input_file /中的所有元素分隔。
  • 在后端,所有输出文件都将打开,因此使用close函数可避免出现诸如too many files opened之类的错误。
  • 如果任何东西都被零除,它将检查0的值,并在输出中输出NaN

答案 1 :(得分:1)

由于您用python-3.x标记了问题,因此以下是一个脚本,可以实现您想要的功能(由于f-strings,它需要Python 3.6 +):

from pathlib import Path
import csv

source = Path('input.tsv')

with source.open() as src:
    csvreader = csv.reader(src, dialect='excel-tab')

    # get number of columns and rewind
    cols = len(next(csvreader)[1:])
    src.seek(0)

    csvwriters = []

    # create a csv.writer for each column
    for i in range(cols):
        # output_col_01.tsv, output_col_02.tsv ...
        csvwriters.append(
            csv.writer(
                Path(f'output_col_{i + 1:02d}.tsv').open('w'),
                dialect='excel-tab'
            )
        )

    nan = float('nan')

    for name, *cols in csvreader:
        for i, a in enumerate(cols):
            row = [name]
            for j, b in enumerate(cols):
                # skip the quotient of a col by itself
                if i != j:
                    a = float(a)
                    b = float(b)
                    # nan if division by zero
                    row.append(round(a / b, 4) if b else nan)

            csvwriters[i].writerow(row)

我没有为除数为0.001的操作添加0,而是选择返回float('nan')

它不会将列本身分开,而是将商四舍五入到小数点后四位。

最后,如果您使用的Python版本早于3.6(由于pathlib.Path(),您仍需要Python版本3.4+),然后替换以下行:

Path(f'output_col_{i + 1:02d}.tsv').open('w'),

具有:

Path('output_col_%02d.tsv' % (i + 1)).open('w'),

这是必须的,因为f-strings是在Python 3.6中引入的。