2010-12-17 90 views
5

我想知道如果下面的代碼可以寫得更好一些。基本上,我想爲(x, y) meshgrid計算z = f(x, y)重寫一個更好,也許更短的方式雙循環

a = linspace(0, xr, 100)                 
b = linspace(0, yr, 100)                 

for i in xrange(100): 
    for j in xrange(100): 
     z[i][j] = f(a[i],b[j]) 

回答

15

是的。你在問題中提出的代碼很好。

不要以爲幾行就是「好」或「酷」。重要的是清晰度,可讀性和可維護性。其他人應該能夠理解你的代碼(你應該在12個月內瞭解它,當你需要找到一個bug時)。許多程序員,尤其是年輕人,都需要「聰明」的解決方案。他們不是。這對Python社區來說非常棒。我們受這種錯誤的困擾比其他人少得多。

+1

我upvoted這一點,因爲它是我學習自己一個教訓,但如果他們只是列出那麼我認爲我的解決方案比OP的代碼更好,因爲它迴避外來迭代器。在相同的假設下,wheatie的答案更好。 – aaronasterling 2010-12-18 00:01:18

+1

迭代器仍然存在。它不會迴避任何事情。這就是說,我一直使用列表推導,當然,但如果我必須將它們嵌套在一起,我將它們展開,以便它們更易於閱讀。 – 2010-12-18 07:32:44

5

你可以做類似

z = [[f(item_a, item_b) for item_b in b] for item_a in a] 
2

如果你把它一下子,你可以用一個列表理解;

[[f(a[i], b[j]) for j in range(100)] for i in range(100)] 

如果您需要使用z是已經存在的,但是,你不能做到這一點,你的代碼是你會得到最整潔。

增加:我不知道這個lingrid是幹什麼的,但是如果它產生一個100個元素的列表,使用aaronasterling的列表理解;沒有必要創建額外的迭代器。

4

你可以使用itertools「產品:

[f(i,j) for i,j in product(a, b)] 

,如果你真的想那些5條線路縮入1,則:

[f(i,j) for i,j in product(linspace(0,xr,100), linspace(0,yr,100)] 

把它更進一步,如果你想要的功能xryr您也可以將0和100的範圍預設爲其他值:

def ranged_linspace(_start, _end, _function): 
    def output_z(xr, yr): 
     return [_function(i, j) for i,j in product(linspace(_start, xr, _end), linspace(_start, yr, _end))] 
    return output_z 
+0

應該指出的是,所有這些解決方案都會產生一個一維列表,而不是像OP的解決方案那樣的嵌套列表。 – aaronasterling 2010-12-18 00:32:54

0

這顯示了一般結果。 a被製成一個6長的列表,並且b是4長的。結果是6個列表的列表,每個嵌套列表長4個元素。

>>> def f(x,y): 
...  return x+y 
... 
>>> a, b = list(range(0, 12, 2)), list(range(0, 12, 3)) 
>>> print len(a), len(b) 
6 4 
>>> result = [[f(aa, bb) for bb in b] for aa in a] 
>>> print result 
[[0, 3, 6, 9], [2, 5, 8, 11], [4, 7, 10, 13], [6, 9, 12, 15], [8, 11, 14, 17], [10, 13, 16, 19]] 
0

我想這就是你要找

z = [[a+b for b in linspace(0,yr,100)] for a in linspace(0,xr,100)] 
0

linspace的一行代碼,實際上看起來可能是np.linspace。如果是,你可以在numpy的數組操作,而無需顯式地重複:

z = f(x[:, np.newaxis], y) 

例如:

>>> import numpy as np 
>>> x = np.linspace(0, 9, 10) 
>>> y = np.linspace(0, 90, 10) 
>>> x[:, np.newaxis] + y # or f(x[:, np.newaxis], y) 
array([[ 0., 10., 20., 30., 40., 50., 60., 70., 80., 90.], 
     [ 1., 11., 21., 31., 41., 51., 61., 71., 81., 91.], 
     [ 2., 12., 22., 32., 42., 52., 62., 72., 82., 92.], 
     [ 3., 13., 23., 33., 43., 53., 63., 73., 83., 93.], 
     [ 4., 14., 24., 34., 44., 54., 64., 74., 84., 94.], 
     [ 5., 15., 25., 35., 45., 55., 65., 75., 85., 95.], 
     [ 6., 16., 26., 36., 46., 56., 66., 76., 86., 96.], 
     [ 7., 17., 27., 37., 47., 57., 67., 77., 87., 97.], 
     [ 8., 18., 28., 38., 48., 58., 68., 78., 88., 98.], 
     [ 9., 19., 29., 39., 49., 59., 69., 79., 89., 99.]]) 

但你也可以使用np.ogrid,而不是兩個linspace

進口numpy的作爲np

>>> x, y = np.ogrid[0:10, 0:100:10] 
>>> x + y # or f(x, y) 
array([[ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90], 
     [ 1, 11, 21, 31, 41, 51, 61, 71, 81, 91], 
     [ 2, 12, 22, 32, 42, 52, 62, 72, 82, 92], 
     [ 3, 13, 23, 33, 43, 53, 63, 73, 83, 93], 
     [ 4, 14, 24, 34, 44, 54, 64, 74, 84, 94], 
     [ 5, 15, 25, 35, 45, 55, 65, 75, 85, 95], 
     [ 6, 16, 26, 36, 46, 56, 66, 76, 86, 96], 
     [ 7, 17, 27, 37, 47, 57, 67, 77, 87, 97], 
     [ 8, 18, 28, 38, 48, 58, 68, 78, 88, 98], 
     [ 9, 19, 29, 39, 49, 59, 69, 79, 89, 99]]) 

它有些依賴你在f是什麼。如果它包含像math.sin功能則需要通過numpy.sin替換它們。

如果它不是關於numpy,那麼你應該堅持要麼你的選擇或循環時,可以選擇使用enumerate

for idx1, ai in enumerate(a): 
    for idx2, bj in enumerate(b): 
     z[idx1][idx2] = f(ai, bj) 

這樣做,你不需要硬編碼的range(或xrange)或優勢使用len(a)作爲輸入。但總的來說,如果沒有巨大的性能差異然後使用你和使用你的代碼別人會很容易理解的方法。


如果abnumpy.array當時的會有一個顯著的性能差異,因爲numpy的可能,如果沒有list <更快的處理陣列 - 要求>numpy.array轉換。