慕田峪7331174
您的代码很慢有两个原因:您做了不必要的工作,并使用了低效的 Python 语句。您不使用veh_id但仍用于int()转换它。您创建一个空字典只是为了在单独的语句中在其中设置 4 个键,您使用单独的str()和 round()调用以及字符串连接,其中字符串格式化可以一步完成所有工作,您重复引用.attrib,因此 Python 必须重复查找该字典属性为你。当用于每个单独的 (x, y) 坐标时,sumolib.net.convertXY2LonLat()实现效率非常低;pyproj.Proj()它每次都从头开始加载偏移量和对象。pyproj.Proj()例如,我们可以通过缓存实例来切断这里的重复操作。或者我们可以避免使用它,或者通过一步处理所有坐标来使用它一次。第一个问题可以通过删除不必要的工作和缓存属性字典之类的东西、只使用一次以及在函数参数中缓存重复的全局名称查找来避免(本地名称使用起来更快);关键字纯粹是_...为了避免查找全局变量:from operator import itemgetter_fields = ('CO2', 'CO', 'NOx', 'PMx')def aggregate( vehicle, _fields=_fields, _get=itemgetter(*_fields, 'x', 'y'), _conv=net.convertXY2LonLat,): # convert all the fields we need to floats in one step *values, x, y = map(float, _get(vehicle.attrib)) # convert the coordinates to latitude and longitude lng, lat = _conv(x, y) # get the aggregation dictionary (start with an empty one if missing) data = raw_pollution_data.setdefault( f"{lng:.4f},{lat:.4f}", dict.fromkeys(_fields, 0.0) ) # and sum the numbers for f, v in zip(_fields, values): data[f] += v为了解决第二个问题,我们可以用至少重用Proj()实例的东西来替换位置查找;在这种情况下,我们需要手动应用位置偏移:proj = net.getGeoProj()offset = net.getLocationOffset()adjust = lambda x, y, _dx=offset[0], _dy=offset[1]: (x - _dx, y - _dy)def longlat(x, y, _proj=proj, _adjust=adjust): return _proj(*_adjust(x, y), inverse=True)然后通过替换_conv本地名称在聚合函数中使用它:def aggregate( vehicle, _fields=_fields, _get=itemgetter(*_fields, 'x', 'y'), _conv=longlat,): # function body stays the same这仍然会很慢,因为它要求我们(x, y)分别转换每一对。这取决于所使用的确切投影,但您可以简单地量化x并y坐标自己进行分组。您将首先应用偏移量,然后将坐标“四舍五入”,转换和舍入将实现的量相同。在投影(1, 0)和(0, 0)取经度差时,我们知道投影使用的粗略转换率,然后将其除以 10.000 就可以得出聚合区域的大小x和y值: (proj(1, 0)[0] - proj(0, 0)[0]) / 10000对于标准的 UTM 投影,它给了我大约11.5,因此将x和y坐标乘以该因子应该可以得到大致相同数量的分组,而不必对每个时间步长数据点进行完整的坐标转换:proj = net.getGeoProj()factor = abs(proj(1, 0)[0] - proj(0, 0)[0]) / 10000dx, dy = net.getLocationOffset()def quantise(v, _f=factor): return v * _f // _fdef aggregate( vehicle, _fields=_fields, _get=itemgetter(*_fields, 'x', 'y'), _dx=dx, _dy=dy, _quant=quantise,): *values, x, y = map(float, _get(vehicle.attrib)) key = _quant(x - _dx), _quant(y - _dy) data = raw_pollution_data.setdefault(key, dict.fromkeys(_fields, 0.0)) for f, v in zip(_fields, values): data[f] += v对于问题中共享的非常有限的数据集,这给了我相同的结果。但是,如果投影在经度上不同,这可能会导致地图上不同点的结果失真。我也不知道您究竟需要如何聚合整个区域的车辆坐标。如果您真的只能按经度和纬度 1/10000 度的区域进行聚合,那么如果您将整个 numpy 数组输入到net.convertXY2LonLat(). 这是因为接受数组来批量pyproj.Proj()转换坐标,节省了大量时间,避免进行数十万次单独的转换调用,我们只需要进行一次调用。与其使用 Python 字典和浮点对象来处理这个问题,不如在这里真正使用 Pandas DataFrame。它可以轻松地从每个元素属性字典中获取字符串(使用具有所有所需键的operator.itemgetter()对象可以非常快速地为您提供这些值),并在摄取数据时将所有这些字符串值转换为浮点数。这些值以紧凑的二进制形式存储在连续内存中,11800 行坐标和数据条目在这里不会占用太多内存。因此,首先将您的数据加载到 DataFrame中,然后从该对象中一步转换您的 (x, y) 坐标,然后使用Pandas 分组功能按区域聚合值:from lxml import etreeimport pandas as pdimport numpy as npfrom operator import itemgetterdef extract_attributes(context, fields): values = itemgetter(*fields) for _, elem in context: yield values(elem.attrib) elem.clear() while elem.getprevious() is not None: del elem.getparent()[0] del contextdef parse_emissions(filename): context = etree.iterparse(filename, tag="vehicle") # create a dataframe from XML data a single call coords = ['x', 'y'] entries = ['CO2', 'CO', 'NOx', 'PMx'] df = pd.DataFrame( extract_attributes(context, coords + entries), columns=coords + entries, dtype=np.float) # convert *all coordinates together*, remove the x, y columns # note that the net.convertXY2LonLat() call *alters the # numpy arrays in-place* so we don’t want to keep them anyway. df['lng'], df['lat'] = net.convertXY2LonLat(df.x.to_numpy(), df.y.to_numpy()) df.drop(coords, axis=1, inplace=True) # 'group' data by rounding the latitude and longitude # effectively creating areas of 1/10000th degrees per side lnglat = ['lng', 'lat'] df[lnglat] = df[lnglat].round(4) # aggregate the results and return summed dataframe return df.groupby(lnglat)[entries].sum()emissions = parse_emissions("/path/to/emission_output.xml")print(emissions)使用 Pandas、一个示例 sumo 网络定义文件和一个重构的 XML 文件,通过重复您的 2 个示例时间步长条目 5900 次,我可以在大约 1 秒(总时间)内解析整个数据集。但是,我怀疑您的 11800 次集数太低(因为它小于 10MB XML 数据),所以我将 11800 * 20 == 236000 次样本写入文件,并且使用 Pandas 处理需要 22 秒。您还可以查看GeoPandas,它可以让您按地理区域进行汇总。