[英]is there a more efficient way to convert string to dictionary than using two for loops?
[英]Is there a more pythonic/more efficient way to loop through dictionary containing lists rather than using for loops?
在使用get
以JSON
格式從API
提取信息之后,我現在嘗試以有效的方式計算price
的平均值。
data
(來自API調用的示例響應):
...
{u'status': u'success', u'data': {u'context_id': u'2', u'app_id': u'123', u'sales': [{u'sold_at': 133, u'price': u'1.8500', u'hash_name': u'Xuan881', u'value': u'-1.00000'}, {u'sold_at': 139, u'price': u'2.6100', u'hash_name': u'Xuan881', u'value': u'-1.00000'},
... etc.
我已設法使用以下代碼執行此操作:
len_sales = len(data["data"]["sales"])
total_p = 0
for i in range(0,len_sales):
total_p += float(data["data"]["sales"][i]["price"])
average = total_p/len_sales
print average
但是,由於檢索到的data
字典很大,因此在顯示輸出之前似乎有相當多的等待時間。
因此,我想知道是否有更高效和/或pythonic的方式來實現相同的結果,但是在更短的時間內。
首先,你沒有循環通過一個字典,你正在循環一個恰好在dict中的列表。
其次,為列表中的每個值執行某些操作本身就需要訪問列表中的每個值; 沒有辦法繞線性成本。
因此,唯一可用的是微優化,這可能不會有太大的區別 - 如果你的代碼太慢,10%的速度沒有幫助,如果你的代碼已經足夠快,你不需要它 - 但有時他們是需要的。
在這種情況下,幾乎所有的微優化也使你的代碼更具可讀性和Pythonic,所以沒有充分的理由不這樣做:
首先,您要訪問data["data"]["sales"]
兩次。 它的性能成本可能是微不足道的,但它也使你的代碼可讀性降低,所以讓我們解決這個問題:
sales = data["data"]["sales"]
接下來,而不是for i in range(0, len_sales):
循環for i in range(0, len_sales):
只是為了使用sales[i]
,它更快 - 而且,更可讀 - 只是循環sales
:
for sale in sales:
total_p += float(sale["price"])
現在我們可以將這個循環變成一個理解,這稍微有點效率(雖然這部分取消了添加生成器的成本 - 你可能真的想測試這個):
prices = (float(sale["price"]) for sale in sales)
...並將其直接傳遞給sum
:
total_p = sum(float(sale["price"]) for sale in sales)
我們也可以使用Python附帶的mean
函數而不是手動執行:
average = statistics.mean(float(sale["price"]) for sale in sales)
...除了你顯然使用Python 2,所以你需要安裝PyPI的非官方backport (官方stats
backport只返回3.1; 2.x版本被放棄),所以讓我們跳過那部分。
把它們放在一起:
sales = data["data"]["sales"]
total = sum(float(sale["price"]) for sale in sales)
average = total / len(sales)
一對夫婦的事情, 可能會幫助,如果它很重要,你一定會想與測試timeit
:
您可以使用operator.itemgetter
獲取price
項。 這意味着您的表達式現在只鏈接兩個函數調用,這意味着您可以鏈接兩個map
調用:
total = sum(map(float, map(operator.itemgetter("price"), sales)))
對於那些不是來自Lisp背景的人而言,這可能不如對任何人的理解那么可讀,但它肯定不是很糟糕,而且可能會快一些。
或者,對於中等大小的輸入,建立臨時列表有時是值得的。 當然,你浪費時間分配內存和復制數據,但迭代列表比迭代生成器更快,所以唯一可靠的方法是測試。
可能有所作為的另一件事是將整個事物轉變為一個功能。 頂級代碼沒有局部變量,只有全局變量,而且查找速度較慢。
如果你真的需要擠出最后幾個百分點,有時甚至值得復制全局和內置函數,比如float
到本地。 當然這對map
沒有幫助(因為我們只訪問過一次),但是理解它可能,所以我將展示如何做到這一點:
def total_price(sales):
_float = float
pricegetter = operator.itemgetter("price")
return sum(map(_float, map(pricegetter, sales)))
對代碼進行基准測試的最佳方法是使用timeit
模塊 - 或者,如果您正在使用IPython,則使用%timeit
magic。 其工作方式如下:
In [3]: %%timeit
... total_p = 0
... for i in range(0,len_sales):
... total_p += float(data["data"]["sales"][i]["price"])
10000 loops, best of 3: 28.4 µs per loop
In [4]: %timeit sum(float(sale["price"]) for sale in sales)
10000 loops, best of 3: 18.4 µs per loop
In [5]: %timeit sum(map(float, map(operator.itemgetter("price"), sales)))
100000 loops, best of 3: 16.9 µs per loop
In [6]: %timeit sum([float(sale["price"]) for sale in sales])
100000 loops, best of 3: 18.2 µs per loop
In [7]: %timeit total_price(sales)
100000 loops, best of 3: 17.2 µs per loop
因此,在我的筆記本電腦上,您的樣本數據:
sales
循環並使用生成器表達式而不是語句大約快35%。 map
和itemgetter
而不是genexpr大約快10%。 map
原因,我們只對每個名稱進行了一次查找,因此我們只需添加一小筆開銷即可獲得0個優惠。) 總的來說, sum(map(…map(…)))
在我的筆記本電腦上被證明是這個特定輸入的禁食。
但是你當然希望用真正的輸入在你的真實環境中重復這個測試。 當小到10%的差異很重要時,您不能只假設細節會轉移。
還有一件事:如果你真的需要加快速度,通常最簡單的方法是使用完全相同的代碼並在PyPy中運行它而不是通常的CPython解釋器。 重復上述一些測試:
In [4]: %timeit sum(float(sale["price"]) for sale in sales)
680 ns ± 19.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [5]: %timeit sum(map(float, map(operator.itemgetter("price"), sales)))
800 ns ± 24.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [6]: %timeit sum([float(sale["price"]) for sale in sales])
694 ns ± 24.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
現在生成器表達式版本是最快的 - 但更重要的是,所有三個版本的速度大約是CPython的20倍。 2000%的改善比35%的改善好很多。
您可以使用名為statistics的庫並查找銷售列表的平均值。 要獲得銷售清單,您可以進行清單理解 -
prices = [float(v) for k, v in i.iteritems() for i in data["data"]["sales"] if k == "price"]
這將為您提供價格清單。 現在你需要做的就是上面的庫了
mean(prices)
或者,你可以做一些像 -
mean_price = sum(prices) / len(prices)
你會得到平均價格。 使用列表推導,您已經優化了代碼。 請參閱此內容並閱讀答案的最后一段
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.