【问题标题】:Sorting a columns value from a large csv(more than a million records) using awk or bash使用 awk 或 bash 对大型 csv(超过一百万条记录)中的列值进行排序
【发布时间】:2017-11-18 21:02:22
【问题描述】:

我是 shell 脚本的新手。

我有一个巨大的 csv 文件,其中包含超过 100k 行。我需要找到一个列并对其进行排序并将其写入另一个文件,然后我需要处理这个新文件。 以下是样本数据

"PT3QB789TSUIDF371261","THE TORONTO,DOMINION BANK","HZSN7FQBPO5IEWYIGC72","MAS,CA.ON.OSC,ASIC*,AAAA","XVCCCCCCCCCCYYUUUUU"
"11111111111111111111","ABC,XYZ,QWE","HZSN7FQBPO5IEWYIGC72","POU,ABC,MAS,CA.QC.OSC,CA.ON.OSC","XVRRRRRRRRTTTTTTTTTTTTT"
"22222222222222222222","BHC,NBC,MKY","HZSN7FQBPO5IEWYIGC72","BVC,AZX,CA.SK.FCAA,CA.NL.DSS","QQQQQQQQQRRCGHDKLKSLS"

现在您可以看到字段 4 的数据也包含逗号。现在我需要将字段 4 整理出来的数据如下:

"PT3QB789TSUIDF371261","THE TORONTO,DOMINION BANK","HZSN7FQBPO5IEWYIGC72","AAAA,ASIC*,CA.ON.OSC,MAS","XVCCCCCCCCCCYYUUUUU"
"11111111111111111111","ABC,XYZ,QWE","HZSN7FQBPO5IEWYIGC72","ABC,CA.ON.OSC,CA.QC.OSC,MAS,POU","XVRRRRRRRRTTTTTTTTTTTTT"
"22222222222222222222","BHC,NBC,MKY","HZSN7FQBPO5IEWYIGC72","AZX,BVC,CA.NL.DSS,CA.SK.FCAA","QQQQQQQQQRRCGHDKLKSLS"

为了获得这个解决方案,我编写了一个如下的脚本文件,但该解决方案似乎效率不高,因为 100k 条记录需要 20 分钟,因此尝试获得有效的解决方案

#this command replaces the comma inside "" with | so that I can split the line based on ','(comma)
awk -F"\"" 'BEGIN{OFS="\""}{for(i=1;i<=NF;++i){ if(i%2==0) gsub(/,/, "|", $i)}} {print $0}' $FEED_FILE > temp.csv

count=0;
while read line
do
       #break the line on comma ',' and get the array of strings.
           IFS=',' read -ra data <<< "$line" #'data' is the array of the record of full line.
           #take the 8th column, which is the reportable jurisdiction.  
            echo "REPORTABLE_JURISDICTION is : " ${data[4]}
            #brake the data based on pipe '|' and sort the data
                IFS='|' read -ra REPORTABLE_JURISDICTION_ARR <<< "${data[4]}"
                    #Sort this array
                    IFS=$'\n' sorted=($(sort <<<"${REPORTABLE_JURISDICTION_ARR[*]}"))           
                    #printf "[%s]\n" "${sorted[@]}"
                    separator="|" # e.g. constructing regex, pray it does not contain %s
                    regex="$( printf "${separator}%s" "${sorted[@]}" )"
                    regex="${regex:${#separator}}" # remove leading separator
                    echo "${regex}"
            data[4]=${regex}
            echo "$data[68]"
            #here we are building the whole line which will be written to the output file.
            separator="," # e.g. constructing regex, pray it does not contain %s
                    regex="$( printf "${separator}%s" "${data[@]}" )"
                    regex="${regex:${#separator}}" # remove leading separator
                        echo "${regex}" >> temp2.csv
        echo $count
        ((count++))    
done < temp.csv

#remove the '|' from the and put the comma back
awk -F\| 'BEGIN{OFS=","} {$1=$1; print}' temp2.csv > temp3.csv
# to remove the tailing , if any
sed 's/,$//' temp3.csv > $OUT_FILE

如何让它更快?

【问题讨论】:

    标签: bash csv awk sed


    【解决方案1】:

    python3 中试用 pandas。唯一的限制:数据需要适合内存。这可能比您的实际数据大一点。我使用 this script, which I quickly wrote: 对 30.000.000 行的 CSV 文件进行了排序,没有任何问题

    import pandas as pd
    import os, datetime, traceback
    
    L1_DIR = '/mnt/ssd/ASCII/'
    suffix = '.csv
    
    for fname in sorted(os.listdir(L1_DIR)):
        if not fname.endswith(suffix):
            continue
        print("Start processing %s" % fname)
        s = datetime.datetime.now()
        fin_path = os.path.join(L1_DIR, fname)
        fname_out = fname.split('.')[0] + '.csv_sorted'
        fpath_out = os.path.join(L1_DIR, fname_out)
    
        df = pd.read_csv(fin_path)
        e = datetime.datetime.now()
        print("Read %s rows from %s. Took (%s)" % (len(df.index), fname, (e-s)))
    
        s = datetime.datetime.now()
        df.set_index('ts', inplace=True)
        e = datetime.datetime.now()
        print("set_index %s rows from %s. Took (%s)" % (len(df.index), fname, (e-s)))
    
        s = datetime.datetime.now()
        df.sort_index(inplace=True)
        e = datetime.datetime.now()
        print("sort_index %s rows from [%s] to [%s]. Took (%s)" % (len(df.index), fname, fname_out, (e-s)))
    
        s = datetime.datetime.now()
        df.reset_index(inplace=True)
        # This one saves at ~10MB per second to disk.. One day is 7.5GB --> 750 seconds or 12.5 minutes
        df.to_csv(fpath_out, index=False)
        e = datetime.datetime.now()
        print("to_csv %s rows from [%s] to [%s]. Took (%s)" % (len(df.index), fname, fname_out, (e - s)))
    
    

    【讨论】:

      【解决方案2】:

      awk

      awk 'BEGIN{ FS=OFS="\042,\042"}{ split($4,a,","); asort(a); sf=a[1]; 
           for(i=2;i<=NF;i++) { sf=sf","a[i] } $4=sf; print $0 }' file > output.csv
      

      output.csv 内容:

      "PT3QB789TSUIDF371261","THE TORONTO,DOMINION BANK","HZSN7FQBPO5IEWYIGC72","AAAA,ASIC*,CA.ON.OSC,MAS,","XVCCCCCCCCCCYYUUUUU"
      "11111111111111111111","ABC,XYZ,QWE","HZSN7FQBPO5IEWYIGC72","ABC,CA.ON.OSC,CA.QC.OSC,MAS,POU","XVRRRRRRRRTTTTTTTTTTTTT"
      "22222222222222222222","BHC,NBC,MKY","HZSN7FQBPO5IEWYIGC72","AZX,BVC,CA.NL.DSS,CA.SK.FCAA,","QQQQQQQQQRRCGHDKLKSLS"
      

      • FS=OFS="\042,\042" - 将"," 视为字段分隔符

      • split($4,a,",") - 通过分隔符将第 4 个字段拆分为数组 ,

      • asort(a) - 按值对数组进行排序

      【讨论】:

        【解决方案3】:

        您在执行任务时使用了错误的工具。虽然 CSV 看起来非常简单,您可以使用 shell 工具轻松处理它,但是您的代码会因包含新行的单元格而中断。在处理大量数据时,bash 也不是很快。

        试试像http://csvkit.rtfd.org/这样直接理解CSV的工具,或者使用像Python这样的编程语言。这允许您在不启动外部进程的情况下完成任务,语法更具可读性并且结果将更易于维护。注意:由于初始成本低,我建议使用 Python。

        使用 python 和csv module,上面的代码将如下所示:

        import csv
        FEED_FILE = '...'
        OUT_FILE = '...'
        with open(OUT_FILE, 'w', newline='') as out:
            with open(FEED_FILE, newline='') as in:
                reader = csv.reader(in, delimiter=',', quotechar='"')
                writer = csv.writer(
                for row in reader:
                    row[3] = sorted(list(row[3].split(',')))
                    writer.writerow(row)
        

        也就是说,您的代码没有明显的问题。在加速 awk 和 sed 方面,您无能为力,而且据我所知,主 bash 循环不会产生许多外部进程。

        【讨论】:

        • @Aron Dingulla ...感谢您的回复,只想问一下这个python代码是否在没有 csv 模块的情况下工作,如果没有 csv 模块的任何解决方案是可能的,请告诉我。如果这是python中的整个代码,那还有一件事可以帮助我实现我的要求?
        • @user2779202:为什么要避免使用csv 模块?它是 Python 发行版的一部分。
        • 当你说:我建议使用 Python 是因为初始成本低。,你认为像 Perl 这样的语言有更高的初始成本还是因为你更清楚 Python 能做什么?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-06
        • 1970-01-01
        • 2021-11-27
        相关资源
        最近更新 更多