CSV文件包含带括号的列(偶尔带逗号)的崩溃pandas.read_csv

时间:2019-04-08 20:01:58

标签: python pandas csv

我需要编写一个解析非常大的.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列,我可以手动合并在一起。

3 个答案:

答案 0 :(得分:3)

如评论中所述,您可以使用“断”的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

答案 1 :(得分:2)

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

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自动用作系列名称的字段;尽管您将不得不进行一些类型检查或指定,因为熊猫会将所有条目视为字符串。

答案 2 :(得分:0)

感谢您提出的只是搜索和替换的建议。效果很好。下面添加了代码,以防万一其他人遇到这种类型的问题。

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 )