【问题标题】:Python. Pandas. BigData. Messy TSV file. How to wrangle the data?Python。熊猫。大数据。凌乱的 TSV 文件。如何整理数据?
【发布时间】:2016-09-02 03:05:51
【问题描述】:

所以。我们有一个 混乱 数据存储在一个 TSV 文件中,我需要对其进行分析。 看起来是这样的

status=200  protocol=http   region_name=Podolsk datetime=2016-03-10 15:51:58    user_ip=0.120.81.243    user_agent=Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36    user_id=7885299833141807155 user_vhost=tindex.ru    method=GET  page=/search/

问题是一些行有不同的列顺序/其中一些缺失值,我需要以高性能摆脱它(因为我正在使用的数据集高达 100 GB)。

Data = pd.read_table('data/data.tsv', sep='\t+',header=None,names=['status', 'protocol',\
                                                     'region_name', 'datetime',\
                                                     'user_ip', 'user_agent',\
                                                     'user_id', 'user_vhost',\
                                                     'method', 'page'], engine='python')
Clean_Data = (Data.dropna()).reset_index(drop=True)

现在我摆脱了缺失值,但一个问题仍然存在! 这是数据的外观:

这就是问题的样子:

如您所见,有些列是偏移的。 我做了一个性能非常低的解决方案

ids = Clean_Data.index.tolist()
for column in Clean_Data.columns:
    for row, i in zip(Clean_Data[column], ids):
        if np.logical_not(str(column) in row):
            Clean_Data.drop([i], inplace=True)
            ids.remove(i)

所以现在数据看起来不错……至少我可以使用它! 但是我上面提出的方法的高性能替代方案是什么?

unutbu 代码更新:回溯错误

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-52c9d76f9744> in <module>()
      8     df.index.names = ['index', 'num']
      9 
---> 10     df = df.set_index('field', append=True)
     11     df.index = df.index.droplevel(level='num')
     12     df = df['value'].unstack(level=1)

/Users/Peter/anaconda/lib/python2.7/site-packages/pandas/core/frame.pyc in set_index(self, keys, drop, append, inplace, verify_integrity)
   2805             if isinstance(self.index, MultiIndex):
   2806                 for i in range(self.index.nlevels):
-> 2807                     arrays.append(self.index.get_level_values(i))
   2808             else:
   2809                 arrays.append(self.index)

/Users/Peter/anaconda/lib/python2.7/site-packages/pandas/indexes/multi.pyc in get_level_values(self, level)
    664         values = _simple_new(filled, self.names[num],
    665                              freq=getattr(unique, 'freq', None),
--> 666                              tz=getattr(unique, 'tz', None))
    667         return values
    668 

/Users/Peter/anaconda/lib/python2.7/site-packages/pandas/indexes/range.pyc in _simple_new(cls, start, stop, step, name, dtype, **kwargs)
    124                 return RangeIndex(start, stop, step, name=name, **kwargs)
    125             except TypeError:
--> 126                 return Index(start, stop, step, name=name, **kwargs)
    127 
    128         result._start = start

/Users/Peter/anaconda/lib/python2.7/site-packages/pandas/indexes/base.pyc in __new__(cls, data, dtype, copy, name, fastpath, tupleize_cols, **kwargs)
    212             if issubclass(data.dtype.type, np.integer):
    213                 from .numeric import Int64Index
--> 214                 return Int64Index(data, copy=copy, dtype=dtype, name=name)
    215             elif issubclass(data.dtype.type, np.floating):
    216                 from .numeric import Float64Index

/Users/Peter/anaconda/lib/python2.7/site-packages/pandas/indexes/numeric.pyc in __new__(cls, data, dtype, copy, name, fastpath, **kwargs)
    105             # with a platform int
    106             if (dtype is None or
--> 107                     not issubclass(np.dtype(dtype).type, np.integer)):
    108                 dtype = np.int64
    109 

TypeError: data type "index" not understood

熊猫版本:0.18.0-np110py27_0

更新

一切正常...谢谢大家!

【问题讨论】:

    标签: python pandas numpy data-analysis bigdata


    【解决方案1】:

    假设您有这样的 TSV 数据:

    status=A    protocol=B  region_name=C   datetime=D  user_ip=E   user_agent=F    user_id=G
    user_id=G   status=A    region_name=C   user_ip=E   datetime=D  user_agent=F    protocol=B
    protocol=B      datetime=D  status=A    user_ip=E   user_agent=F    user_id=G
    

    字段顺序可能乱码,可能有缺失值。但是,您不必因为字段未按特定顺序出现而删除行。您可以使用行数据本身中提供的字段名称将值放置在正确的列中。例如,

    import pandas as pd
    
    df = pd.read_table('data/data.tsv', sep='\t+',header=None, engine='python')
    df = df.stack().str.extract(r'([^=]*)=(.*)', expand=True).dropna(axis=0)
    df.columns = ['field', 'value']
    
    df = df.set_index('field', append=True)
    df.index = df.index.droplevel(level=1)
    df = df['value'].unstack(level=1)
    
    print(df)
    

    产量

    field datetime protocol region_name status user_agent user_id user_ip
    index                                                                
    0            D        B           C      A          F       G       E
    1            D        B           C      A          F       G       E
    2            D        B        None      A          F       G       E
    

    要处理一个大的 TSV 文件,你可以分块处理行,然后在最后将处理后的块连接到一个 DataFrame 中:

    import pandas as pd
    
    chunksize =     # the number of rows to be processed per iteration
    dfs = []
    reader = pd.read_table('data/data.tsv', sep='\t+',header=None, engine='python',
                           iterator=True, chunksize=chunksize)
    for df in reader:
        df = df.stack().str.extract(r'([^=]*)=(.*)', expand=True).dropna(axis=0)
        df.columns = ['field', 'value']
        df.index.names = ['index', 'num']
    
        df = df.set_index('field', append=True)
        df.index = df.index.droplevel(level='num')
        df = df['value'].unstack(level=1)
        dfs.append(df)
    
    df = pd.concat(dfs, ignore_index=True)
    print(df)
    

    解释:给定df

    In [527]: df = pd.DataFrame({0: ['status=A', 'user_id=G', 'protocol=B'],
     1: ['protocol=B', 'status=A', 'datetime=D'],
     2: ['region_name=C', 'region_name=C', 'status=A'],
     3: ['datetime=D', 'user_ip=E', 'user_ip=E'],
     4: ['user_ip=E', 'datetime=D', 'user_agent=F'],
     5: ['user_agent=F', 'user_agent=F', 'user_id=G'],
     6: ['user_id=G', 'protocol=B', None]}); df
       .....:    .....:    .....:    .....:    .....:    .....:    .....: 
    Out[527]: 
                0           1              2           3             4             5           6
    0    status=A  protocol=B  region_name=C  datetime=D     user_ip=E  user_agent=F   user_id=G
    1   user_id=G    status=A  region_name=C   user_ip=E    datetime=D  user_agent=F  protocol=B
    2  protocol=B  datetime=D       status=A   user_ip=E  user_agent=F     user_id=G        None
    

    您可以将所有值合并到一个列中

    In [449]: df.stack()
    Out[449]: 
    0  0         status=A
       1       protocol=B
       2    region_name=C
       3       datetime=D
       4        user_ip=E
       5     user_agent=F
       6        user_id=G
    1  0        user_id=G
       1         status=A
       2    region_name=C
       3        user_ip=E
       4       datetime=D
       5     user_agent=F
       6       protocol=B
    2  0       protocol=B
       1       datetime=D
       2         status=A
       3        user_ip=E
       4     user_agent=F
       5        user_id=G
    dtype: object
    

    然后应用.str.extract(r'([^=]*)=(.*)')将字段名与值分开:

    In [450]: df = df.stack().str.extract(r'([^=]*)=(.*)', expand=True).dropna(axis=0); df
    Out[450]: 
                   0  1
    0 0       status  A
      1     protocol  B
      2  region_name  C
      3     datetime  D
      4      user_ip  E
      5   user_agent  F
      6      user_id  G
    1 0      user_id  G
      1       status  A
      2  region_name  C
      3      user_ip  E
      4     datetime  D
      5   user_agent  F
      6     protocol  B
    2 0     protocol  B
      1     datetime  D
      2       status  A
      3      user_ip  E
      4   user_agent  F
      5      user_id  G
    

    为了更容易引用 DataFrame 的各个部分,让我们为列和索引级别提供描述性名称:

    In [530]: df.columns = ['field', 'value']; df.index.names = ['index', 'num']; df
    Out[530]: 
                     field value
    index num                   
    0     0         status     A
          1       protocol     B
    ...
    

    现在如果我们将field 列移动到索引中:

    In [531]: df = df.set_index('field', append=True); df
    Out[531]: 
                          value
    index num field            
    0     0   status          A
          1   protocol        B
          2   region_name     C
          3   datetime        D
    ...
    

    并删除 num 索引级别:

    In [532]: df.index = df.index.droplevel(level='num'); df
    Out[532]: 
                      value
    index field            
    0     status          A
          protocol        B
          region_name     C
          datetime        D
    ... 
    

    那么我们就可以得到所需形式的DataFrame 通过将field 索引级别移动到列索引中:

    In [533]: df = df['value'].unstack(level=1); df
    Out[533]: 
    field datetime protocol region_name status user_agent user_id user_ip
    index                                                                
    0            D        B           C      A          F       G       E
    1            D        B           C      A          F       G       E
    2            D        B        None      A          F       G       E
    

    【讨论】:

    • 能否请您评论您的代码,以便我(和每个人)可以完全关注您?嗯,看起来 iPython 处理该代码的速度真的很慢。实际上它卡在df = df['all'].str.extract('\t'.join(['(.*)']*(n+1)), expand=True) 行...有什么想法吗?
    • 我已经更改了代码(因此它不再使用该行)并添加了有关如何分块读取 TSV 文件的部分。如果块大小不是太大,您应该能够更快地看到结果。而且,也许你不需要形成一个巨大的DataFrame;也许您可以迭代地处理 TSV 块。
    • df = df.set_index('field', append=True) throws TypeError: data type "index" not understood... 你可以试试我的Data file (just 30Mb)... 其实我不知道为什么。似乎是熊猫错误
    • 我下载了data.tsv,在上面运行了我的代码,但无法重现TypeError。我正在使用熊猫版本 0.18.0。请发布(在您的问题中,而不是 cmets 部分)完整的回溯错误消息。它会告诉我们有用的信息,例如引发 TypeError 的确切行。
    • 问题可能与github.com/pydata/pandas/issues/13000有关。我已经修改了代码以避免重新分配df.index.names。也许看看这是否避免了TypeError
    【解决方案2】:

    你可以使用 Pandas 的vectorized string operations,特别是str.contains

    import numpy as np
    
    # boolean index of rows to keep
    is_valid = np.ones(Clean_data.shape[0], np.bool)
    
    for column in Clean_Data.columns:
    
        # check whether rows contain this column name
        is_valid &= Clean_Data[column].str.contains(column)
    
    # drop rows where not all elements contain their respective column names
    Clean_Data.drop(np.where(~is_valid)[0], inplace=True)
    

    【讨论】:

    • --------------------------------------------------------------------------- ValueError Traceback (most recent call last) &lt;ipython-input-357-1c9ef7b6faef&gt; in &lt;module&gt;() 7 8 # check whether rows contain this column name ----&gt; 9 is_valid &amp;= Clean_Data[column].str.contains(column) 10 11 # drop rows where not all elements contain their respective column names ValueError: operands could not be broadcast together with shapes (10,) (75618,) (10,)
    • 对不起,is_valid的初始化有错别字。立即尝试。
    【解决方案3】:

    我无法添加 cmets,所以我将其作为回复发布(实际上这是对您关于内存使用和运行时的评论的回复)。

    对于大文件 (100GB),您需要考虑的一件事是您不会将这些文件读入内存。您可以为 pandas “Large data” work flows using pandasHow to read a 6 GB csv file with pandas 设置块大小,或者使用带有 csv 模块的 yield 生成器并逐行读取文件。 Reading a huge .csv in python

    结合@unutbu 关于使用正则表达式将条目排序到列中的评论,考虑到每个单元格的字段名是如此清晰地划分(即r'(.*)=(.*)' 是所有必需的 - 尽管可能需要一些错误更正)应该是您所需要的一切(正如他们所说,由于某些数据丢失而删除整行并不是典型的或推荐的方法)。

    【讨论】:

      猜你喜欢
      • 2022-01-20
      • 1970-01-01
      • 1970-01-01
      • 2019-07-08
      • 1970-01-01
      • 2017-01-16
      • 1970-01-01
      • 2019-06-24
      相关资源
      最近更新 更多