前段时间需要分析一下玩家行为。拿到的数据有2G大小。130个字段
需求是统计玩家在执行某个事件前做了那些事。频率按高到低排序
想也不想就开始写。读取csv。按照用户分类。再按照时间排序。
结果运行才开始就报错。不是溢出就是虚拟内存不足闪退,后面虽然运行起来了但是又太慢
pandas.read_csv 读取内存溢出
经过查询发现是可以分块读取的,直接在参数上加上chunksize即可指定每次读取大小
这个分块是指行数。例如填上100就是每次读取100行
参数设置iterator为True使结果是可迭代的
reader = pd.read_csv(file_name,header=0,iterator=True)
reader.get_chunk(10000)
字段太多生成命令字符串过慢
为了加速查询数据。我希望读写到数据库里面,但是我发现130个字段生成插入命令实在太耗时了
为了生成命令。使用 ThreadPool 开了多条线程同时创建命令。在线程处理完成后写入命令到数据库
#链接数据库
conn = sqlite3.connect(database_name)
c = conn.cursor()
#创建表
c.execute("create table if not exists data(%s)" % (','.join(text)))
conn.commit()
#创建线程池
pool = ThreadPool(threadNum)
tempitems = []
while True:
try:
chunk = reader.get_chunk(chunk_size)
tempitems.append(ChunkItem(headTable, chunk))
#如果读取的块数量等于线程数丢入线程处理
if len(tempitems) >= threadNum:
pool.map(itemToCommand, tempitems)
#创建完成后写入数据库
for item in tempitems:
for command in item.result:
c.execute(command)
conn.commit()
itemToCommand=[]
except StopIteration:
break
conn.close()
pandas 的 nan
在读取过程中 pandas 遇到空值会设置为nan。但我并不需要。把空值过滤设置为空就好
reader = pd.read_csv(file_name, header=0, iterator=True, encoding='utf-8', dtype=headTable, low_memory=False, na_filter=False)
sqlite查询速度
前面已经把数据写入数据。现在就需要对数据查询。
根据需求我应该先对用户ID分组。然后对分组后的数据进行排序。最后查询是否有执行某个事件。再根据策划提供的条件判断这条数据是否符合要求。最后在获取在执行该事件前做了什么。最常做的又是什么
第一步分组。sqlite 提供了 order by 命令对数据分组,下面根据user字段对表进行分组
cursor.execute("select * from data order by user")
然后再对用户数据按照时间排序
cursor.execute("select * from data order by time")
当然我们不可能分开写。这样得到的结果不过是2个不同的数据集,把命令合并先对时间排序然后按照用户分组
cursor.execute("select * from (select * from data order by time) order by user")
此时即可得到正确的数据集,但是作为一个有千万数据的数据库这么执行一次将会耗费非常长时间。我也想过分页,但是每次运行都会全表排序一次。耗时实在太长无法接受。
sqlite创建临时表
经过查询数据库可以把结果集赋值给一个新的表。因为当前需要分析的数据只需要一次写入就完成。以后的数据不会再插入。所以可以直接把结果集赋值给新的表。后面的查询操作在新表就行。这大大加快了查询速度
cursor.execute("create table sort as select * from (select * from data order by time) order by user")
排序后的表已经有了。那么就需要判断一下这个表是不是存在。毕竟不可能每次都去生成一下
cursor.execute("SELECT count(*) FROM sqlite_master WHERE type="table" AND name = "sort"")
sqlite 索引
查询一个用户的行动轨迹我只需要查询一下即可
cursor.execute("select * from sort where user='%s'"%id)
但是每次查询都会耗时1秒以上。假设需要查询的用户有7000个,那么就需要将近2小时这也太可怕了
经查询给数据表增加索引即可加速查询。只需要在创建表后给表加上索引就好
cursor.execute("create index id on sort('user','event')")
加上索引后确实能体验飞一般的速度
第一运行将会建立数据库,创建排序表。之后再去查询。比较长时间外。之后再去查询就非常快了