【问题标题】:How to preprocess and load a "big data" tsv file into a python dataframe?如何预处理“大数据”tsv 文件并将其加载到 python 数据框中?
【发布时间】:2017-01-16 20:40:36
【问题描述】:

我目前正在尝试将以下大型制表符分隔文件导入 Python 中的类似数据框的结构中——当然,我使用的是 pandas 数据框,尽管我对其他选项持开放态度。

此文件大小为几 GB,不是标准的 tsv 文件 --- 它已损坏,即行具有不同的列数。一行可能有 25 列,另一行有 21 列。

以下是数据示例:

Col_01: 14 .... Col_20: 25    Col_21: 23432    Col_22: 639142
Col_01: 8  .... Col_20: 25    Col_22: 25134    Col_23: 243344
Col_01: 17 .... Col_21: 75    Col_23: 79876    Col_25: 634534    Col_22: 5    Col_24: 73453
Col_01: 19 .... Col_20: 25    Col_21: 32425    Col_23: 989423
Col_01: 12 .... Col_20: 25    Col_21: 23424    Col_22: 342421    Col_23: 7    Col_24: 13424    Col_25: 67
Col_01: 3  .... Col_20: 95    Col_21: 32121    Col_25: 111231

如您所见,其中一些列的顺序不正确...

现在,我认为将此文件导入数据框的正确方法是预处理数据,以便您可以输出具有NaN 值的数据框,例如

Col_01 .... Col_20    Col_21    Col22    Col23    Col24    Col25
8      .... 25        NaN       25134    243344   NaN      NaN
17     .... NaN       75        2        79876    73453    634534
19     .... 25        32425     NaN      989423   NaN      NaN
12     .... 25        23424     342421   7        13424    67
3      .... 95        32121     NaN      NaN      NaN      111231

更复杂的是,这是一个非常大的文件,有几 GB 大小。

通常,我会尝试分块处理数据,例如

import pandas as pd

for chunk in pd.read_table(FILE_PATH, header=None, sep='\t', chunksize=10**6):
    # place chunks into a dataframe or HDF 

但是,我看不到先以块的形式“预处理”数据,然后使用块将数据读入pandas.read_table() 的方法。你会怎么做?有哪些可用的预处理工具——也许是sedawk?

这是一个具有挑战性的问题,因为数据的大小和在加载到数据帧之前必须完成的格式设置。任何帮助表示赞赏。

【问题讨论】:

  • 您能否预先提供列名的“最大”(最完整的一个 - 即包括所有可能的列名)列表?
  • @MaxU 使用的所有列名的详尽列表?是的,我想是的
  • 不确定我是否正确理解了这个问题,但我们在“脏”文件中遇到了类似的问题,我们使用 R 清理数据,然后将新文件传递给 Spark 进行进一步处理。
  • 这是一个开始:stackoverflow.com/questions/39331907/… 删除创建 for 循环的字母表并使用帖子末尾所述的列标题。
  • 我可能应该,这是 2 天内第三次需要它。等一下,这里快午夜了。

标签: python pandas awk sed dataframe


【解决方案1】:
$ cat > pandas.awk
BEGIN {
    PROCINFO["sorted_in"]="@ind_str_asc" # traversal order for for(i in a)                  
}
NR==1 {       # the header cols is in the beginning of data file
              # FORGET THIS: header cols from another file replace NR==1 with NR==FNR and see * below
    split($0,a," ")                  # mkheader a[1]=first_col ...
    for(i in a) {                    # replace with a[first_col]="" ...
        a[a[i]]
        printf "%6s%s", a[i], OFS    # output the header
        delete a[i]                  # remove a[1], a[2], ...
    }
    # next                           # FORGET THIS * next here if cols from another file UNTESTED
}
{
    gsub(/: /,"=")                   # replace key-value separator ": " with "="
    split($0,b,FS)                   # split record from ","
    for(i in b) {
        split(b[i],c,"=")            # split key=value to c[1]=key, c[2]=value
        b[c[1]]=c[2]                 # b[key]=value
    }
    for(i in a)                      # go thru headers in a[] and printf from b[]
        printf "%6s%s", (i in b?b[i]:"NaN"), OFS; print ""
}

数据样本 (pandas.txt):

Col_01 Col_20 Col_21 Col_22 Col_23 Col_25
Col_01: 14  Col_20: 25    Col_21: 23432    Col_22: 639142
Col_01: 8   Col_20: 25    Col_22: 25134    Col_23: 243344
Col_01: 17  Col_21: 75    Col_23: 79876    Col_25: 634534    Col_22: 5    Col_24: 73453
Col_01: 19  Col_20: 25    Col_21: 32425    Col_23: 989423
Col_01: 12  Col_20: 25    Col_21: 23424    Col_22: 342421    Col_23: 7    Col_24: 13424    Col_25: 67
Col_01: 3   Col_20: 95    Col_21: 32121    Col_25: 111231

$ awk -f pandas.awk -pandas.txt
Col_01 Col_20 Col_21 Col_22 Col_23 Col_25
    14     25  23432 639142    NaN    NaN 
     8     25    NaN  25134 243344    NaN 
    17    NaN     75      5  79876 634534 
    19     25  32425    NaN 989423    NaN 
    12     25  23424 342421      7     67 
     3     95  32121    NaN    NaN 111231 

所有需要的列都应该在数据文件头中。在处理时收集标头可能不是一项大工作,只需将数据保存在数组中并最终打印,可能在版本 3 中。

如果您从与数据文件 (pandas.txt) 不同的文件 (cols.txt) 中读取标头,请执行脚本 (pandas.awk):

$ awk -F pandas.awk cols.txt pandas.txt

【讨论】:

  • 这是一个很好的解决方案,谢谢!从不同文件中读取标题行Col_01 Col_20 Col_21 Col_22 Col_23 Col_25 的最AWK-ish 方式是什么?
  • 可能将NR==1 替换为FNR==NR 并以{... next} 结束以下块。见代码中的 cmets。
  • AND:运行它:awk -F pandas.awk cols.txt pandas.txt。文件cols.txt 中的列标题需要用空格分隔。
  • ...就像您在pandas.txt 标头中看到的那样,忘记了Col_24 不会输出...
  • 晚安。我明天评论一下。
【解决方案2】:

另一个版本,它以单独的列文件作为参数或使用第一条记录。无论哪种方式运行:

awk -f pandas2.awk pandas.txt # first record as header
awk -f pandas2.awk cols.txt pandas.txt # first record from cols.txt
awk -v cols="cols.txt" -f pandas2.awk pandas.txt # read cols from cols.txt

甚至:

awk -v cols="pandas.txt" -f pandas2.awk pandas.txt # separates keys from pandas.txt for header

代码:

$ cat > pandas2.awk
BEGIN {
    PROCINFO["sorted_in"]="@ind_str_asc" # traversal order for for(i in a)
    if(cols) {                           # if -v cols="column_file.txt" or even "pandas.txt"
        while ((getline line< cols)>0) { # read it in line by line
            gsub(/: [^ ]+/,"",line)      # remove values from "key: value"
            split(line,a)                # split to temp array
            for(i in a)                  # collect keys to column array
                col[a[i]]
        }
        for(i in col)                    # output columns
            printf "%6s%s", i, OFS
        print ""
    }
}
NR==1 && cols=="" {                      # if the header cols are in the beginning of data file
                                         # if not, -v cols="column_file.txt"
    split($0,a," +")                     # split header record by spaces
    for(i in a) {
        col[a[i]]                        # set them to array col
        printf "%6s%s", a[i], OFS        # output the header
    }
    print ""
}
NR==1 {
    next
}
{
    gsub(/: /,"=")                       # replace key-value separator ": " with "="
    split($0,b,FS)                       # split record from separator FS
    for(i in b) {
        split(b[i],c,"=")                # split key=value to c[1]=key, c[2]=value
        b[c[1]]=c[2]                     # b[key]=value
    }
    for(i in col)                        # go thru headers in col[] and printf from b[]
        printf "%6s%s", (i in b?b[i]:"NaN"), OFS; print ""
}

【讨论】:

  • @ShanZhengYang 第2版,看看吧。
  • 对不起,令人难以置信的延误---真的要感谢你!我之前对标题行的处理方式感到困惑。这是学习 awk 的好方法!
  • 你的超能力在这里可能有用:) : stackoverflow.com/questions/39398986/…
【解决方案3】:

您可以在 Pandas 中更彻底地做到这一点。

假设你有两个独立的数据框,只有一个重叠的列:

>>> df1
   A  B
0  1  2
>>> df2
   B  C
1  3  4

您可以使用.concat 将它们连接在一起:

>>> pd.concat([df1, df2])
    A  B   C
0   1  2 NaN
1 NaN  3   4

您可以看到NaN 是为不存在的行值创建的。

这可以很容易地应用于您的示例数据,而无需进行任何预处理:

import pandas as pd
df=pd.DataFrame()
with open(fn) as f_in:
    for i, line in enumerate(f_in):
        line_data=pd.DataFrame({k.strip():v.strip() 
                  for k,_,v in (e.partition(':') 
                        for e in line.split('\t'))}, index=[i])
        df=pd.concat([df, line_data])

>>> df
  Col_01 Col_20 Col_21  Col_22  Col_23 Col_24  Col_25
0     14     25  23432  639142     NaN    NaN     NaN
1      8     25    NaN   25134  243344    NaN     NaN
2     17    NaN     75       5   79876  73453  634534
3     19     25  32425     NaN  989423    NaN     NaN
4     12     25  23424  342421       7  13424      67
5      3     95  32121     NaN     NaN    NaN  111231

或者,如果您的主要问题是在多块添加列中建立所需的列顺序,只需先读取所有列值(未测试):

# based on the alpha numeric sort of the example of:
# [ALPHA]_[NUM]
headers=set()
with open(fn) as f:
    for line in f:
        for record in line.split('\t'):
            head,_,datum=record.partition(":")
            headers.add(head)
# sort as you wish:             
cols=sorted(headers, key=lambda e: int(e.partition('_')[2])) 

如果在 DataFrame 的初始创建中给出,Pandas 将使用列表的顺序作为列顺序。

【讨论】:

  • 谢谢。考虑到问题,最后一行不起作用——假设我们有一个 1T 大小的文件,并将其存储在 HDF5 文件中到磁盘。我们将无法将df=pd.concat[df, line_data]) 保存到 RAM 中。解析数据后,我们使用for line in pd.table(): store("HDF5").append(line),然后通过store.create_table()保存,参见上面我对“块”的讨论。
  • 但是,我认为您可以使用line_data 作为块,然后像往常一样保存到 HDF5。你能解释一下这如何处理(1)不等数量的字段行和(2)错误顺序的字段吗? (例如,第一行有 1、24、22、21 列,下一行有 1、21、22、23 等)
  • 添加新列的设置索引操作后,列顺序基于字母数字排序顺序。在这种特殊情况下,使用您的示例数据,效果很好。如果列的顺序不是你想要的,你可以在最后用df=df[desired_order]改变它
  • 所以,我一直在尝试上面的代码,但输入文件是“二进制”。嗯,有什么想法吗?我一直在这里尝试选项:stackoverflow.com/questions/39861431/…
  • 什么样的二进制文件?您需要了解文件格式。只是utf-8 还是其他形式的编码?如果是这样,请在打开文件时使用正确的解码器,系统会向您显示逻辑上正确的流。
猜你喜欢
  • 1970-01-01
  • 2016-09-02
  • 2020-07-24
  • 2020-04-15
  • 1970-01-01
  • 2021-10-03
  • 2020-07-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多