分析用的Python内存表数据结构(字典、列表、组合)
我正在尝试用Python模拟一些我已经在SQL中工作的代码。之前有人帮我解答过这个问题,关于如何将CSV文件转换成包含所有列名的Python字典。
现在我可以把压缩的CSV文件读入一个字典里,但只读到了最后一行。(我该怎么获取一些行的样本或者整个数据文件呢?)
我希望能有一个在内存中的表格,像在SQL中那样可以操作,比如通过匹配错误数据和另一个包含错误数据的表来清理数据,然后按类型汇总,按时间段平均等等。整个数据文件大约有50万行。我并不在意是否把所有数据都放到内存中,但我想尽量解决一般情况,这样我就知道在不使用SQL的情况下可以做到什么。
import csv, sys, zipfile
sys.argv[0] = "/home/tom/Documents/REdata/AllListing1RES.zip"
zip_file = zipfile.ZipFile(sys.argv[0])
items_file = zip_file.open('AllListing1RES.txt', 'rU')
for row in csv.DictReader(items_file, dialect='excel', delimiter='\t'):
pass
# Then is my result is
>>> for key in row:
print 'key=%s, value=%s' % (key, row[key])
key=YEAR_BUILT_DESC, value=EXIST
key=SUBDIVISION, value=KNOLLWOOD
key=DOM, value=2
key=STREET_NAME, value=ORLEANS RD
key=BEDROOMS, value=3
key=SOLD_PRICE, value=
key=PROP_TYPE, value=SFR
key=BATHS_FULL, value=2
key=PENDING_DATE, value=
key=STREET_NUM, value=3828
key=SOLD_DATE, value=
key=LIST_PRICE, value=324900
key=AREA, value=200
key=STATUS_DATE, value=3/3/2011 11:54:56 PM
key=STATUS, value=A
key=BATHS_HALF, value=0
key=YEAR_BUILT, value=1968
key=ZIP, value=35243
key=COUNTY, value=JEFF
key=MLS_ACCT, value=492859
key=CITY, value=MOUNTAIN BROOK
key=OWNER_NAME, value=SPARKS
key=LIST_DATE, value=3/3/2011
key=DATE_MODIFIED, value=3/4/2011 12:04:11 AM
key=PARCEL_ID, value=28-15-3-009-001.0000
key=ACREAGE, value=0
key=WITHDRAWN_DATE, value=
>>>
我觉得我可能走错了方向……
首先,我只读到了大约50万行数据文件中的一行。其次,字典可能不是合适的结构,因为我觉得我不能直接加载所有50万行并对它们进行各种操作,比如按组和日期求和。而且,重复的键可能会导致问题,比如县和子区这些不唯一的描述符。
我也不知道怎么把特定的小部分行读入内存(比如先测试10行或100行,然后再加载全部数据,我也不太明白这个过程……)我看过Python的文档和几本参考书,但就是没能理解。
我发现大多数答案都建议使用各种SQL解决方案来处理这种情况,但我很想学习如何用Python实现类似的结果。我觉得在某些情况下,这样做会更简单、更快,也能扩展我的工具箱。不过,我很难找到相关的例子。
有一个答案暗示了我想要的方向:
一旦读取正确,DictReader应该可以用来将行转换为字典,这是一种典型的按行结构。奇怪的是,这通常不是处理你这种查询的高效方式;只有列列表会让搜索变得更简单。按行结构意味着你必须为每一行重新进行一些查找工作。像日期匹配这样的事情需要一些在CSV中并不存在的数据,比如日期是如何表示的,哪些列是日期。
以下是获取列导向数据结构的一个例子(不过需要加载整个文件):
import csv
allrows=list(csv.reader(open('test.csv')))
# Extract the first row as keys for a columns dictionary
columns=dict([(x[0],x[1:]) for x in zip(*allrows)])
The intermediate steps of going to list and storing in a variable aren't necessary.
The key is using zip (or its cousin itertools.izip) to transpose the table.
Then extracting column two from all rows with a certain criterion in column one:
matchingrows=[rownum for (rownum,value) in enumerate(columns['one']) if value>2]
print map(columns['two'].__getitem__, matchingrows)
When you do know the type of a column, it may make sense to parse it, using appropriate
functions like datetime.datetime.strptime.
来自Yann Vernier
肯定有一些关于这个主题的好参考资料吧?
3 个回答
Numpy(数值计算库)是处理和比较数组的最佳工具,而你的表格基本上就是一个二维数组。
你说:“我现在可以把我的压缩的CSV文件读入一个字典里了,不过只有一行,最后一行。(我怎么才能获取一些行或者整个数据文件呢?)”
你的代码是这样做的:
for row in csv.DictReader(items_file, dialect='excel', delimiter='\t'):
pass
我不太明白你为什么这么写,但这样做的结果是逐行读取整个输入文件,却忽略了每一行(pass
的意思是“什么都不做”)。最后的结果是,row
只指向最后一行(当然,如果文件是空的,那就没有最后一行了)。
要想“获取”整个文件,你可以把 pass
改成 do_something_useful_with(row)
。
如果你想把整个文件读入内存,只需这样做:
rows = list(csv.DictReader(.....))
如果你想获取一个样本,比如每隔N行取一行(N > 0),从第M行开始(0 <= M < N),可以这样做:
for row_index, row in enumerate(csv.DictReader(.....)):
if row_index % N != M: continue
do_something_useful_with(row)
顺便说一下,你不需要 dialect='excel'
; 这是默认设置。
你只能一次从csv读取器读取一行数据,但你可以很容易地把所有数据存储在内存中:
rows = []
for row in csv.DictReader(items_file, dialect='excel', delimiter='\t'):
rows.append(row)
# rows[0]
{'keyA': 13, 'keyB': 'dataB' ... }
# rows[1]
{'keyA': 5, 'keyB': 'dataB' ... }
然后,你可以进行汇总和计算:
sum(row['keyA'] for row in rows)
在把数据放入rows
之前,你可能想先对数据进行一些转换,或者使用更友好的数据结构。每次计算都要遍历50万行数据,这样可能会变得非常低效。
正如评论者提到的,使用内存数据库对你来说可能会非常有帮助。另一个问题正好询问了如何将csv数据转移到sqlite数据库中。
import csv
import sqlite3
conn = sqlite3.connect(":memory:")
c = conn.cursor()
c.execute("create table t (col1 text, col2 float);")
# csv.DictReader uses the first line in the file as column headings by default
dr = csv.DictReader(open('data.csv', delimiter=','))
to_db = [(i['col1'], i['col2']) for i in dr]
c.executemany("insert into t (col1, col2) values (?, ?);", to_db)