我有一个CSV文件,我需要在python中阅读和处理。
CSV文件包含以下表格值:
*aa
1 foo1 foo_bar1
2 foo2 foo_bar2
*bb
1.22 bla1 blabla1 blablabla22
1.33 bla2 ' ' blablabla33
此处aa
和bb
是每个表的名称。无论表名出现在何处,名称前面都有*,下面的行是该表的行。
请注意,每个表都可以:
但是,我们确切知道csv文件中存在哪些表(即表名)
我需要读取csv文件并将表的整个内容分配给一个变量。我可以想到一种蛮力的做法。但是,由于python有一个带有读写操作的csv模块,是否有任何内置功能可以使我更容易或更有效?
注意:到目前为止我遇到的一个主要问题是,在使用csv.reader()
读取csv文件后,我看到aa的行有额外的空列。我相信这是因为aa和bb列的数量不匹配。我还想删除这些额外的空列,而不删除代表实际缺失值的空列。
答案 0 :(得分:1)
最干净的方法是在将每个组提供给csv阅读器之前分离表。这是一个粗略的开始,让你开始:
from itertools import takewhile
import csv
# Instead of *s*, you can use an open file object here
s = '''\
*aa
1,foo1,foo_bar1
2,foo2,foo_bar2
*bb
1.22,bla1,blabla1,blablabla22
1.33,bla2, ,blablabla33
'''.splitlines()
it = iter(s)
next(it)
for table in ['aa', 'bb']:
print(f'\nTable: {table}')
for row in csv.reader(takewhile(lambda r: not r.startswith('*'), it)):
print(row)
这会产生:
Table: aa
['1', 'foo1', 'foo_bar1 ']
['2', 'foo2', 'foo_bar2']
Table: bb
['1.22', 'bla1', 'blabla1', 'blablabla22']
['1.33', 'bla2', ' ', 'blablabla33']
答案 1 :(得分:0)
您是否考虑过使用熊猫?
import pandas as pd
df = pd.read_csv('foo.csv', sep=r'/s+', header=None) #if there is table headings, remove header = None
您不需要在文件顶部添加任何行。 这会将具有不同行数和列数的文件读入数据帧。您现在可以在其中执行各种操作。例如: 空元素由NaN表示,这意味着不是数字。您可以使用''仅写入
替换它df.fillna(' ')
为了适合您的用例,根据我的理解,您在同一个csv文件中有多个表,请尝试:
df = pd.read_csv("foo.csv", header=None, names=range(3))
table_names = ["*aa", "*bb", "*cc"..]
groups = df[0].isin(table_names).cumsum()
tables = {g.iloc[0,0]: g.iloc[1:] for k,g in df.groupby(groups)}
这将创建一个表的列表,其中key作为表名和值,就像表本身一样。
for k,v in tables.items():
print("table:", k)
print(v)
print()
您可以在documentation.
中找到更多详情答案 2 :(得分:0)
您可以像解析csv文件那样检查第一个值是否以'*'
开头并从中构建dict
。
import csv
from collections import defaultdict
import pprint
csv_data = defaultdict(list)
with open('data.csv', 'r') as csv_file:
# filter empty lines
csv_reader = csv.reader(filter(lambda l: l.strip(',\n'), csv_file))
header = None
for row in csv_reader:
if row[0].startswith('*'):
header = row[0]
else:
# additional row processing if needed
csv_data[header].append(row)
pprint.pprint(csv_data)
# Output
defaultdict(<class 'list'>,
{'*aa': [['1', ' foo1', 'foo_bar1', ''],
['2', ' foo2', 'foo_bar2', '']],
'*bb': [['1.22', ' bla1', 'blabla1', 'blablabla22'],
['1.333', ' bla2', '', 'blablabla3']]})
如果由于另一个较大的元素较大而要从表中删除多余的元素,则一个选项是
csv_data[header].append(row[:col_nums[header]])
你提到的地方你知道你的桌子应该有多少列
col_nums = {'*aa' : 3, '*bb' : 4}
defaultdict(<class 'list'>,
{'*aa': [['1', ' foo1', 'foo_bar1'],
['2', ' foo2', 'foo_bar2']],
'*bb': [['1.22', ' bla1', 'blabla1', 'blablabla22'],
['1.333', ' bla2', '', 'blablabla3']]})
如果我误读了它,你只知道列的最大数量而不是每个表的列数,那么你可以改为。
def trim_row(row):
for i, item in enumerate(reversed(row)):
if not item:
break
return row[:len(row) - i]
# use it like so
csv_data[header].append(trim_row(row))