接上一篇文章,XML结构依旧,但是这次Vv的条数非常多,10万左右,按照以下第一种python代码运行,竟然需要40分钟之久,完全不可接受,需要寻找原因,改变方法!
<File xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FileFormat.xsd">
<FileHeader>
<DateTime>2016-01-06T03:30:10+08:00DateTime>
FileHeader>
<Objects>
<ObjectType>EutranCellTddObjectType>
<FieldName>
<N i="1">aN>
<N i="2">bN>
<N i="3">cN>
<N i="4">dN>
<N i="5">fN>
<N i="6">gN>
<N i="7">hN>
<N i="8">iN>
<N i="9">jN>
<N i="10">kN>
FieldName>
<FieldValue>
<Vv Label="xxx">
<V i="1">1V>
<V i="2">汉字V>
<V i="3">还是汉字V>
<V i="4">一直是汉字V>
<V i="5">1V>
<V i="6">怎样?V>
<V i="7">V>
<V i="8">V>
<V i="9">V>
<V i="10">{0}V>
Vv>
Objects>
File>
import xml.etree.cElementTree as et
import numpy as np
import pandas as pd
def xml_analysis(config_file, data_file):
# 读取配置文件,得到输入字段名(要从xml文件中提取的字段)和输出字段名(提取后转换并最终输出的字段)
input_field, output_field = LoadConfig(config_file)
# 初始化三个DataFrame,其中df_input_field表示以输入字段名为列,行数为1的empty DataFrame,df_output_field表示以输出字段名为列,不定义行列数的DataFrame,tmp_df_output_field表示以输出字段名为列,行数为1的empty DataFrame
df_input_field = pd.DataFrame(np.empty([1,len(input_field)]), columns=input_field, dtype='str')
df_output_field = pd.DataFrame(columns=output_field, dtype='str')
tmp_df_output_field = pd.DataFrame(np.empty([1,len(df_output_field)]), columns=df_output_field, dtype='str')
# 创建dict_i_name的字典,i为key,name为value
fieldName, fieldValue = XmlDataParse(data_file,'Objects','FieldName', 'FieldValue')
names = fieldName.findall('N')
dict_i_name = GenDict(names, input_field)
# Get FieldValue Node
VV = fieldValue.findall('Vv')
for vv in VV:
# 通过上篇文章的方法,获得一个Vv标签中,所有需要的字段值,放入df_input_field中
df_input_field = DealingSingleValue(cm,dict_i_name,'i',df_input_field)
# 处理单条数据,解析成输出的内容
a = df_input_field['a'][0]
b = df_input_field['b'][0]
d = df_input_field['d'][0]
tmp_df_output_field['x'] = a
tmp_df_output_field['y'] = b
tmp_df_output_field['z'] = d
df_output_field.append(tmp_df_output_field)
# 数据全部处理完,放入df_output_field中后,将该DataFrame写成csv,这里WriteCsv是自己写的函数,调用了DataFrame的to_csv,'xxx'是csv的文件名,返回文件路径+文件名
csv_file = WriteCsv('xxx', df_output_field)
return csv_file
按照此方法运行程序,通过在VV循环中,每1000条数据打一次时间发现,刚开始时每1000条处理时间大概在6秒,越往后越慢,到最后每1000条的处理实际大概是60秒,整个xml文件处理完用了40多分钟,不可接受!
通过观察,猜测是不停的append()导致的时间越来越长。写了一个小的测试例证实了猜想。
记得C++中的vector在不停地加元素后,会出现连续空间不够的情况,于是会分配新的更大的空间给vector,然后将之前的全部复制一遍过去,多次复制粘贴这种写的操作肯定会耗时。
猜测这里速度慢的原因是这样,需要以后找文献看看。
开始考虑直接申请一个固定的超大的空间来装,测试发现速度仍然很慢,
最终选用的解决方案是再增加一个中间容器,其长度是固定的,命名为df_output_field,而最终的输出df改名为big_df_output_field。
每次tmp_df_output_field把中间容器写满后,big_df_output_field在append中间容器,于是修改代码大致如下:
def xml_analysis(config_file, data_file):
ISOTIMEFORMAT='%Y-%m-%d %X'
# 定长的中间容器的行数
MAXLEN = 1000
input_field, output_field = LoadConfig(config_file)
# 多初始化一个中间容器,tmp_df_output_field改为Series类型
df_input_field = pd.DataFrame(np.empty([1,len(input_field)]), columns=input_field, dtype='str')
big_df_output_field = pd.DataFrame(columns=output_field, dtype='str')
tmp_df_output_field = pd.Series(np.empty([len(output_field)]), index=output_field, dtype='str')
df_output_field = pd.DataFrame(np.empty([MAXLEN,len(output_field)]), columns=output_field, dtype='str')
FieldName, FieldValue = XmlDataParse(data_file,'Objects','FieldName', 'FieldValue')
dict_i_name = GenDict(FieldName, input_field)
VV = FieldValue.findall('Vv')
cnt = 0
append_flag = False
for vv in VV:
# 标识是否已经append
append_flag = False
# 每次先清空,防止无值时被上一值覆盖
df_input_field[:] = None
tmp_df_output_field[:] = None
df_input_field = DealingSingleValue(cm,dict_i_name,'i',df_input_field)
a = df_input_field['a']
b = df_input_field['b']
d = df_input_field['d']
tmp_df_output_field['x'] = a
tmp_df_output_field['y'] = b
tmp_df_output_field['z'] = d
df_output_field.ix[cnt%MAXLEN] = tmp_df_output_field
cnt += 1
# 提前自增1,防止cnt为0时进入判断成功部分,加入很多空行,
# 或防止加入条件cnt>0才进入判断,使得第一行数据被覆盖
if cnt % MAXLEN == 0:
# 打印完成条数和当前时间
PrintMsgAndTime('Cnt'+str(cnt),ISOTIMEFORMAT)
big_df_output_field = big_df_output_field.append(df_output_field.copy())
df_output_field[:][:] = None
append_flag = True
# 循环结束后,若最后一个数不能整除,则df_output_field的数据还未append到big_df_output_field后面,需要将之加入
if append_flag==False:
# 注意[0:cnt%MAXLEN-1],因为cnt在最后一个循环内又自增了一次,需要减去
big_df_output_field = big_df_output_field.append(df_output_field.ix[0:cnt%MAXLEN-1])
csv_file = WriteCsv('xxx',big_df_output_field, output_field)
return csv_file
以下是测试结果和思考,
1. 这种方法减小了append的次数,确实让处理速度比较匀速,
2. MAXLEN的取值会影响速度,当MAXLEN很大,如10万的时候,每千条的处理速度比第一种方法起步时的速度慢很多,具体原因不清楚,而MAXLEN过小时,如为1,与之前的相同了,当MAXLEN=1000时,每千条的处理速度基本保持匀速,且总处理时间115秒左右
3. 按照常规考虑,函数y=f(MAXLEN),其中y是每千条处理速度,应该是一个最优化问题,能找到一个MAXLEN,使得y最大,但是哪个才是最优解没有继续尝试。