在 Python 中处理数百万行
Posted
技术标签:
【中文标题】在 Python 中处理数百万行【英文标题】:Handling millions of rows in Python 【发布时间】:2019-03-13 03:53:23 【问题描述】:我想以我已经研究过 Python 时间复杂度和可用于加快处理速度的数据结构这一事实作为这个问题的序言。
但是,我很难想出一种有效的方法来对照 250 万行文件检查一组值。到目前为止,我考虑过的一种解决方案是使用列表推导。
目前,我正在尝试通过以下方式处理:
def getTotalVolumeByCounty(fileName, counties):
values = []
with open(fileName) as csvFile:
csvReader = csv.reader(csvFile)
headers = next(csvReader)
for row in csvReader:
i = 0
while i < len(counties):
if row[9] == counties[i]:
values[i] += int(row[22])
break
return values
如果您愿意,可以选择“传统”方式。将一个列表中的每个值与另一个列表中的当前值进行比较。显然这在时间复杂度方面是不利的。
如前所述,我曾考虑过使用列表推导 - 但这些实际上如何节省时间?列表理解是我当前尝试的唯一选择吗?
【问题讨论】:
能否确认250万行文件中的数据会被排序?while
循环的目的是什么? i
永远不会改变,len(counties)
也不会改变,所以循环永远不会执行。
我可以确认。有 250 万行,我需要对它们进行排序。我必须将每个县的总销量加起来。
另外,values
被初始化为一个空列表,并且从未添加任何元素。因此,如果values[i] += int(row[22])
行确实执行过,那将是一个错误,因为values[i]
不存在。
据我所知,列表推导并不比传统循环快。他们的目的是允许更好看的代码,而不是更快的代码。
【参考方案1】:
如今 250 万行并不多,但您的工作不太正常的代码是将每行检查乘以工作中的县数,这将使其非常慢。如果我们使用您所在县的中间字典,我们可以像这样更快地做到这一点:
def get_total_volume_by_county(file_name, counties):
county_volume_map = county: 0 for county in counties
with open(file_name) as csv:
csv_reader = csv.reader(csv)
headers = next(csv_reader)
for row in csv_reader:
county_volume_map[row[9]] += row[22]
return county_volume_map
如果您真的只想要体积列表,您可以在最后将其转换回来,但县到体积的地图可能更有用。
【讨论】:
【参考方案2】:根据OP上的评论帖,我会在这里添加一个建议。
在处理大量数据时,通常先以某种方式对数据进行排序,然后使用二进制搜索之类的方法来查找数据块,这通常会更有效。
例如,您提到要将一个列表中的项目与第二个列表中的项目进行比较。为此,我将假设第一个列表(列表 A)的大小很小,而第二个列表(列表 B)的大小很大。
如果列表 B 中的项目按某个键排序,例如县名(假设所有县都有唯一的名称),您可以使用 Binary Search Algorithm 在条目块中查找随机(基本上)项目对于县,然后根据任何给定县的条目数,您可以执行 2 次循环来查找上限和下限,或者在不同的键上进行另一个二进制搜索或类似的搜索,通过该键,列表将排在原始位置之后键(例如总体积),这将为您留下与您确定的某些指标相匹配的项目列表。
如果数据尚未排序,则可能值得对其进行排序,因为 Heapsort 或 Quicksort 的时间复杂度最差为 O(nlogn),而二分查找最差为 O(logn)。循环遍历列表的时间复杂度可能是 O(kn^k) 或其他东西,如果你要绘制图表,会差很多倍。
至于你问题的最后一部分,列表理解只是语法糖,并没有做任何特别花哨的事情。
tldr;通过一些唯一标识符对数据进行排序,我建议使用Heapsort,因为它是就地的,通用的,你可以提供一个比较函数,它可以使用它,你可能会在 Python 中查找一个迭代实现。然后使用二分搜索有效地查找项目。
希望这会有所帮助!
【讨论】:
真的,在 Python 中,您通常只需将您的项目放在set
中。在这种情况下,迭代一个 200 万行的文件并检查 countries
的匹配项,其中 countries
是 set
对象将非常快并且代码非常少。
你可以这样做,我只是假设数据将是一些任意字符串,如“CountyName,Volume,Amount,Date,ProductId,...,...”或类似的,它可能会更快如果你追求速度,手动排序而不是尝试依赖 Python 解释器来做那种事情。
嗯?是的,显然他们使用的是 csv。我不认为在 Python 中手动排序会更快。我非常怀疑你会击败用 C 实现的 Timsort。如果你手动排序,你依赖于 python 解释器。
如果您可以为默认排序方法提供自己的比较功能,那么我完全同意。我关于使用排序数据提高速度的观点并不取决于具体的排序方法,只是在搜索之前对数据进行了排序。【参考方案3】:
完全基于您的函数名称和签名,我假设您只是试图按国家/地区对总销售额进行分组,其中countries
是您感兴趣的国家/地区的列表在 Python 中最直接的方法是使用计数的dict
。分组通常使用dict
对象完成。在这种情况下,您的dict
也将用作“设置”,因为我们将为每个国家/地区使用0
初始化字典。然后在增加相应值之前检查国家是否在字典中。
def get_total_volume_by_country(file_name, counties):
volume_by_country = dict.fromkeys(countries, 0)
with open(file_name) as csv_file:
csv_reader = csv.reader(csv_file)
headers = next(csv_reader)
for row in csv_reader:
country = row[9] # presumably country name
if country in volume_by_country:
volume_by_country[country] += int(row[22]) # volume presumably
return volume_by_country
【讨论】:
我的回答几乎是一致的(抱歉 - 我们重叠了;),但您将每个县卷添加到我认为的所有县卷中(县可能等于国家;))。 @PaulWhipp 我不这么认为,volume_by_country[country] += int(row[22])
但我还没有实际测试过这段代码。也许我们对这个问题的解释不同?
@PaulWhipp 啊,也许你错过了我的这行:volume_by_country = dict.fromkeys(countries, 0)
。我们的基本上是等价的,但你假设遇到的每个国家都实际存在于countries
。我假设情况可能并非如此。
@jaunpa.arrivillaga:你是对的,对不起。我将您的条件略读为原始 for 循环。以上是关于在 Python 中处理数百万行的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 pandas 或 python 将具有数百万行的表从 PostgreSQL 复制到 Amazon Redshift