Unix shell脚本-根据另一个csv文件中的头文件从csv文件中选择列(以最小的可重复示例重新发布)

mtb9vblg  于 2023-06-24  发布在  Shell
关注(0)|答案(2)|浏览(115)

把这个问题作为以前的答案重新发布是不起作用的,因为缺乏最小的可重复的例子(mea culpa)。对不起,如果这是基本的,但我不能让它工作,并花了很多时间尝试。
请看我之前发布的问题:Unix shell script select columns in csv file based on headers from another csv file
我创建了一个csv头文件,其中头文件中的每一行都是我想要的列的名称。在data_file.csv本身中,第一行显示如下,每个列标题都在第一行中,数据用引号括起来:

echo $(head -n 1 data_file.csv)
"eid","132421-0.0","132422-0.0","132423-0.0", ...

我创建的头文件如下所示,每个列头都是一行,没有引号。

eid
24500-0.0
24503-0.0
24503-1.0
4526-0.0
4526-1.0

注意没有引号。如果我尝试(手动)将引号添加到headers.csv文件中,然后再次使用$cat,我会在每个标题行上得到三个引号(不知道为什么)。

"""eid"""
"""24500-0.0"""
"""24500-1.0"""
"""24503-0.0"""
"""24503-1.0"""
"""4526-0.0"""
"""4526-1.0"""

我所要做的就是从庞大的data_file.csv(有28,000列)中提取20列,其标题列在headers.csv文件中。然后我可以把它们加载到R中,然后就可以了。
数据本身是字符和数字的混合,每个字段都用引号括起来。
@glenn_jackman提出了以下解决方案,但我没有指出报价:

awk '
    BEGIN {FS = OFS = ","}
    NR == FNR {wanted[$0] = 1; next}
    FNR == 1 {
        ncol = 0
        for (i = 1; i <= NR; i++)
            if ($i in wanted)
                columns[++ncol] = i
    }
    {
        for (i = 1; i <= ncol; i++)
            printf "%s%s", $columns[i], OFS
        print ""
    }
' headers.csv data_file.csv > selected_data_file.csv

因此,此操作失败,我得到一个空白的selected_data_file. csv。
我正在寻找的输出是:

$ cat selected_data_file.csv
"eid", "24500-0.0", "24503-0.0", "24503-1.0", "4526-0.0", "4526-1.0"
"AB1","1","a","0","1.2",""

行数与data_file. csv相同。
不知道如何使它更清晰或更可复制比...非常感谢您的帮助。

oxosxuxt

oxosxuxt1#

从问题和OP的评论中得出的假设/理解:

  • 所有字段(包括标题字段)都用双引号括起来
  • 字段之间用逗号分隔,逗号两侧可能还有空格
  • 字段可以包含逗号
  • 字段不包含嵌入的换行符

根据(上述)假设制作一些样本数据:

$ cat headers.csv
eid
24503-1.0
4526-1.0

$ cat data_file.csv
"eid","24500-0.0", "24503-0.0", "24503-1.0","4526-0.0", "4526-1.0"
"AB1","1","a","0,111","1.2",""
"CD2","2","b","9","","-123,be"

一个GNU awk(用于FPAT支持):

awk '
BEGIN   { FPAT = "([^,]+)|(\"[^\"]+\")" }                   # define field patterns

        # remove following block if we do NOT have to worry about white space before/after the comma delimiter

        { for ( i=1;i<=NF;i++ )                             # for all fields ...
              gsub(/^[[:space:]]+|[[:space:]]+$/,"",$i)     # strip leading/trailing white space
        }

FNR==NR { hdr["\"" $1 "\""]                                 # 1st file: populate array of headers
          next
        }

FNR==1  { for ( i=1;i<=NF;i++ )                             # 2nd file: process header fields
              if ( $i in hdr )                              # if in our hdr[] array then 
                 cols[++colcnt] = i                         # populate array of columns making note of their order
        }
        { for ( i=1;i<=colcnt;i++ )                         # 2nd file: for each data line loop through list of desired columns and print to stdout
              printf "%s%s", $(cols[i]), (i<colcnt ? "," : ORS)
        }
' headers.csv data_file.csv

这产生:

"eid","24503-1.0","4526-1.0"
"AB1","0,111",""
"CD2","9","-123,be"
iaqfqrcu

iaqfqrcu2#

您在评论中指出您正在使用英国生物库数据集。Biobank为Windows和Linux提供了一个转换实用程序ukbconvhttps://biobank.ctsu.ox.ac.uk/crystal/download.cgi
根据this pdf和官方文档,给定文本文件中的字段编号列表,直接从原始文件中提取相关列为适合R的格式的命令为:

ukbconv dataset.enc_ukb r -ifield_list.txt

文档区分了“字段”和“列”:
例如,假设我们只想从数据集中提取字段31、20204和40000,并将其转换为csv格式。我们创建一个名为field_list.txt的文本文件,其内容如下:

31
20204
40000

并将其放入与ukbconv相同的文件夹中。然后我们运行命令:

ukbconv ukb23456.enc_ukb csv -ifield_list.txt

结果输出将仅包含eid列以及Data-Fields 31、20204和40000的所有示例和数组组合的列(假设这些Data-Fields存在于.enc_ukb文件中)。
为了帮助准备提供所需字段列表的文件,ukbconv每次运行时都会输出名为field.ukb的文件,该文件列出了与数据集相关的所有可用字段。这可以被编辑以标识要被包括在子集中或从子集中排除的特定字段。
它还讨论了“示例和数组索引”,并指出在csv的情况下:
列标题的格式为F-I.A,其中F是数据字段编号,I是示例索引,A是数组索引
这看起来像你的问题的头格式,所以它可能是,通过使用ukbconv,你最终会得到更多的数据比你要求的(即。额外的列)。这对你来说可能是个问题,也可能不是。

相关问题