【问题标题】:CSV file containing column with occasional comma in parentheses crashes pandas.read_csv包含括号中偶尔逗号的列的 CSV 文件使 pandas.read_csv 崩溃
【发布时间】:2019-08-30 01:06:06
【问题描述】:

我需要编写一个函数来解析一个非常大的 .csv 文件。不幸的是,制作 csv 文件的人没有使用非常好的分隔符,因为其中一个文本列偶尔会使用分隔符(我无法控制这一点)。 csv 文件在文本周围没有引号,即有问题的行如下所示:

colAVal, (colBVal_1, colBVal_2), colCVal

我应该注意,对于所有有问题的行,B 列的值是标准的,因为它始终是(colBVal_1, colBVal_2)

因为大多数行没有这个问题,pandas.read_csv 预计有 3 列,当它遇到这些有问题的行之一时会崩溃。

因为逗号在括号中,我无法找到解决此问题的方法,请使用 read_csv 中的 quotechar 参数。我想避免编写自己的 read_csv 函数来逐行执行并尽可能手动解决此问题。理想情况下,我正在寻找一种方法来告诉 read_csv 当它遇到(colBVal_1,colBVal_2)时,它应该自动将其分配给 colB 或让 read_csv 将数据分成 2 个数据帧:一个有 3 列,一个有 4 列,我可以手动合并在一起。

【问题讨论】:

  • 最好的办法是自己编写一个解析器,然后将生成的结构输入到 pandas 中。

标签: python pandas csv


【解决方案1】:

感谢您仅搜索和替换的建议。那工作得很好。在下面添加代码以供参考,以防其他人遇到此类问题。

from StringIO import StringIO
import pandas as pd
text = open('file/location', "r")
        text = StringIO(''.join([i for i in text]) \
            .replace("(colBVal_1, colBVal_2)", "(colBVal_1 colBVal_2)"))
        df= pd.read_csv( text )
【解决方案2】:

正如 cmets 中所说,您可以使用“损坏”的 csv 行来制定语法,并将结果输出提供给 pandas DataFrame。
以下内容当然可以优化,但可能会给您一个想法:

from parsimonious.grammar import Grammar
from parsimonious.nodes import NodeVisitor
import pandas as pd

broken_garbage = """
1, (2, 3), 4
colAVal, (colBVal_1, colBVal_2), colCVal,
this, one, right
234,(123,456),789
"""

grammar = Grammar(
    r"""
    content     = garbage? line+
    line        = entry+ newline?
    entry       = value sep?
    value       = word / (lpar word sep word rpar)

    lpar        = "("
    rpar        = ")"
    word        = ~"\w+"
    sep         = ws? "," ws?

    ws          = ~"[\t ]+"
    newline     = ~"[\r\n]+"
    garbage     = (ws / newline)+
    """
)

class BrokenVisitor(NodeVisitor):
    def generic_visit(self, node, visited_children):
        return visited_children or node

    def visit_value(self, node, visited_children):
        child = visited_children[0]
        if isinstance(child, list):
            _, value1, _, value2, _ = child
            return (value1.text, value2.text)
        else:
            return child.text

    def visit_entry(self, node, visited_children):
        values, _ = visited_children
        return values

    def visit_line(self, node, visited_children):
        content = visited_children[0]
        return [item for item in content]

    def visit_content(self, node, visited_children):
        return visited_children[1]

tree = grammar.parse(broken_garbage)

broken = BrokenVisitor()
values = broken.visit(tree)

df = pd.DataFrame(values, columns=["one", "two", "three"])
print(df)


这产生
       one                     two    three
0        1                  (2, 3)        4
1  colAVal  (colBVal_1, colBVal_2)  colCVal
2     this                     one    right
3      234              (123, 456)      789


查看反映您的结构的语法。 BrokenVisitor 类访问每个语法块并将行作为列表返回。然后将此结果输入pandas.DataFrame 构造函数。

或者,您可以使用支持\K 的较新的regex module,并将括号中的所有逗号替换为另一个字符:
\([^,()]+\K,

Python 这可能是:

import regex as re

rx = re.sub(r'\([^,()]+\K,')
new_string = rx.sub('@', old_string)

之后,您可以将新字符串直接输入pandas.read_csv()
a demo on regex101.com

【讨论】:

    【解决方案3】:

    没有看到任何示例数据,很难知道需要什么,但是:

    import re
    import pandas as pd
    
    def my_parser(csv_file)
        with open(csv_file, "r") as fh:
            for line in fh:
                line = line.strip()
    
                if re.match(r".*\(.*,.*\).*", line):
                    # Process line with extra commas
                    # ...
                else:
                    # Process normal line
                    # ...
    
                yield processed_line
    
    
    df = pd.Dataframe(my_parser("file.csv"), ...)
    

    对于处理,您可以尝试仅将括号中的逗号替换为另一个字符。

    我建议使用namedtuple 作为保存processed_line 的结构,因为它们具有pandas 自动使用的字段作为系列名称;虽然您必须进行一些类型检查或指定,因为 pandas 会将所有条目视为字符串。

    【讨论】:

      猜你喜欢
      • 2020-03-02
      • 1970-01-01
      • 2016-12-14
      • 1970-01-01
      • 2023-02-21
      • 2011-05-20
      • 2010-09-27
      • 2023-03-29
      • 1970-01-01
      相关资源
      最近更新 更多