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