【问题标题】:Split huge file into n files keeping first 7 columns + next 3 columns until column n将大文件拆分为 n 个文件,保留前 7 列 + 后 3 列直到第 n 列
【发布时间】:2020-11-29 09:41:09
【问题描述】:

我有一个带有列名的巨大数据框:

A,B,C,D,F,G,H,GT_a,N_a_,E_a,GT_b,N_b_,E_b,GT_c,N_c_,E_c,...,GT_n,N_n,E_n

使用 unix/bash 或 python,我想生成 n 个具有以下列的单个文件:

A,B,C,D,F,G,H,GT_a,N_a_,E_a

A,B,C,D,F,G,H,GT_b,N_b_,E_b

A,B,C,D,F,G,H,GT_c,N_c_,E_c

....

A,B,C,D,F,G,H,GT_n,N_n_,E_n

应调用每个文件:a.txt、b.txt、c.txt、...、n.txt

【问题讨论】:

  • 你尝试了什么?

标签: python bash dataframe unix split


【解决方案1】:

这里有几个使用bash 工具的解决方案。


1.重击

bash 循环中使用cut。这将引发n 进程并解析文件n 次。

更新,对于这种情况,我们在列名中不仅有作为 _id 的字母序列,而且还有许多字符串 id,在前 7 行之后每 3 行重复相同的内容。我们必须首先读取文件的标题并提取它们,例如一个快速的解决方案是使用 awk 并每隔 8、11 等列将它们打印到 bash 数组中。

#!/bin/bash
first=7
#ids=( {a..n} )
ids=( $( head -1 "$1" | awk -F"_" -v RS="," -v f="$first" 'NR>f && (NR+1)%3==0{print $2}' ) )

for i in "${!ids[@]}"; do
    cols="1-$first,$((first+1+3*i)),$((first+2+3*i)),$((first+3+3*i))"
    cut -d, -f"$cols" "$1" > "${ids[i]}.txt"
done

用法:bash test.sh file


2。 awk

或者您可以使用awk。这里我只自定义输出的数量,但其他的也可以像第一个解决方案一样完成。

BEGIN { FS=OFS=","; times=14 } 
{ 
  for (i=1;i<=times;i++) {
    print $1,$2,$3,$4,$5,$6,$7,$(5+3*i),$(6+3*i),$(7+3*i) > sprintf("%c.txt",i+96)
  }
}

用法:awk -f test.awk file.

这个解决方案应该很快,因为它解析文件一次。但它不应该这样使用,对于大量的输出文件,它可能会抛出“打开的文件过多”错误。对于字母的范围,应该没问题。

【讨论】:

  • 谢谢@thanasisp!。但是我的文件有 >10000 列,并且给出的列名只是玩具示例。实际上,列名的格式为:GT_rs1637537、N_rs1637537、E_rs1637537、GT_rs9487596、N_rs9487596、E_rs9487596 等。非常感谢您的帮助
  • 你是对的@thanasisp,你的解决方案是对的。您能告诉我如何将解决方案调整为 GT_rs1637537、GT_rs163753788 等形式的列名吗?非常感谢,给您带来的不便深表歉意。
  • 谢谢@thanasisp!出于某种原因,我不明白,当我应用你的 .sh 脚本时,我得到:“头:无法打开'文件'进行阅读:没有这样的文件或目录”,即使我给出了整个路径:bash test.sh /path/myfile.txt
  • 谢谢@thanasisp。当我用我的文件运行 bash 脚本时,我没有得到任何输出文件
  • @Lucas 我用游览样本输入(不带点“...”)对其进行了测试,我看到 4 个文件的标题按预期创建。
【解决方案2】:
import pandas as pd
import numpy as np

c = "A,B,C,D,F,G,H,GT_a,N_a_,E_a,GT_b,N_b_,E_b,GT_c,N_c_,E_c,GT_d,N_d_,E_d,GT_e,N_e_,E_e".split(',')
df = pd.DataFrame(np.full((30, 22), c), columns=c)

c = None
c = list(df.columns)
default = c[:7]
var = np.matrix(c[7:])
var = pd.DataFrame(var.reshape(var.shape[1]//3, 3))

def dump(row):
    cols = default + list(row)
    magic = cols[-1][-1]
    df[cols].to_csv(magic + '.txt')

var.apply(dump, axis=1)

【讨论】:

  • 非常感谢@volante!但是当你有 >1,000,000 列并且你想用列名创建向量 c 时,你该怎么做呢?
  • 嗨@volante,我的意思是,如果您有>1,000,000 列并且您正在读取一个真实的数据框,而不是作为带有列名的字符串向量。非常感谢
  • @Lucas 答案已更新以从 DataFrame 中提取列名。你是这个意思吗?
【解决方案3】:

这应该写出不同的文件,每个文件都有不同的标题。您必须将 COL_NAMES_TO_WRITE 更改为您想要的。

它使用标准库,所以没有 pandas。它不会写出超过 26 个不同的文件。但可以更改文件名生成器以增加它并允许它。

如果我对这个问题的解释正确,您希望将其拆分为 14 个文件 (a..n)

您必须将下面的代码复制到一个文件中,splitter.py 然后运行这个命令: python3.8 splitter.py --fn largefile.txt -n 14

largefile.txt 是您需要拆分的大文件。

import argparse
import csv
import string

COL_NAMES_TO_WRITE = "A,B,C,D,F,G,H,GT_{letter},N_{letter},E_{letter}"
WRITTEN_HEADERS = set()  # place to keep track of whether headers have been written

def output_file_generator(num):
    if num > 26: raise ValueError(f"Can only print out 26 different files, not {num}")

    i = 0
    while True:
        prefix = string.ascii_lowercase[i]
        i = (i + 1) % num  # increment modulo number of files we want
        yield f"{prefix}.txt"

def col_name_generator(num):
    i = 0
    while True:
        col_suffix = string.ascii_lowercase[i]
        i = (i + 1) % num  # increment modulo number of files we want
        print( COL_NAMES_TO_WRITE.format(letter=col_suffix).split(','))
        yield COL_NAMES_TO_WRITE.format(letter=col_suffix).split(',')

def main(filename, num_files=4):
    """Split a file into multiple files

    Args:
        filename (str): large filename that needs to be split into multiple files
        num_files (int): number of files to split filename into
    """
    print(filename)
    with open(filename, 'r') as large_file_fp:
        reader = csv.DictReader(large_file_fp)
        output_files =  output_file_generator(num_files)
        col_names = col_name_generator(num_files)
        for line in reader:
            print(line)
            filename_for_this_file = output_files.__next__()
            print("filename ", filename_for_this_file)
            column_names_for_this_file = col_names.__next__()
            print("col names:", column_names_for_this_file)

            with open(filename_for_this_file, 'a') as output_fp:

                writer = csv.DictWriter(output_fp, fieldnames=column_names_for_this_file)
                if filename_for_this_file not in  WRITTEN_HEADERS:
                    writer.writeheader()
                    WRITTEN_HEADERS.add(filename_for_this_file)
                just_these_fields = {k:v for k,v in line.items() if k in column_names_for_this_file}
                writer.writerow(just_these_fields)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-fn", "--filename", required=True, default='large_file.txt', help="filename of large file to be split")
    parser.add_argument("-n", "--num_files", required=False, default=4, help="number of separate files to split large_file into")
    args = parser.parse_args()
    main(args.filename, int(args.num_files))

【讨论】:

  • 谢谢@voortuck!。当我运行您的代码时,我得到: "File "split_df_into_snp_files.py", line 12 if num > 26: raise ValueError(f"Can only print out 26 different files, not {num}") ^ SyntaxError: invalid syntax" 。实际上,我想将大文件拆分为 893 个文件(我将 26 替换为 893)。在第 7 列之后,大文件中的所有列都这样调用:GT_rs1637537、N_rs1637537、E_rs1637537、GT_rs9487596、N_rs9487596、E_rs9487596,等等。所以最后我会得到 953 个文件,每个文件有 10 列。
  • 嗨@voortuck,我再次运行它(python3 splitter.py -fn myfile.txt -n 893),我得到: Traceback(最近一次调用最后):文件“splitter.py”,行59,在 main(args.filename, int(args.num_files)) 文件“splitter.py”,第 37 行,在 main for line in reader:文件“/usr/lib/python3.6/csv.py ”,第 111 行,在下一个 self.fieldnames 文件中“/usr/lib/python3.6/csv.py”,第 98 行,在字段名中 self._fieldnames = next(self.reader) _csv.Error:字段大于字段限制(131072)
  • 看起来你有超过 131072 列,超出了 csv 的库能力。您可能需要添加:csv.field_size_limit(sys.maxsize) 如上所述 here
猜你喜欢
  • 2021-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多