2019年1月14日 星期一

NumPy 筆記:平移陣列元素

前言


今天學生問了一個好玩的問題:如果使用已經建立了一個二維陣列,要怎麼做才能將所有的元素向某個方向平移?以下是我找到的作法,請注意,以下的程式碼都省略了 import numpy as np

方法1:使用索引取出元素


產生陣列


首先我們產生一個 $9 \times 8$ 的陣列,為了一眼就看出每個元素原來的位置,我將第0列皆設定為個位數,第1列皆為1開頭,其餘依此類推。

m, n = 9, 8
a = np.zeros(m * n).reshape(m, n)

for i in range(m):
    for j in range(n):
        a[i][j] = i * 10 + j

print(a)

輸出為

[[ 0.  1.  2.  3.  4.  5.  6.  7.]
 [10. 11. 12. 13. 14. 15. 16. 17.]
 [20. 21. 22. 23. 24. 25. 26. 27.]
 [30. 31. 32. 33. 34. 35. 36. 37.]
 [40. 41. 42. 43. 44. 45. 46. 47.]
 [50. 51. 52. 53. 54. 55. 56. 57.]
 [60. 61. 62. 63. 64. 65. 66. 67.]
 [70. 71. 72. 73. 74. 75. 76. 77.]
 [80. 81. 82. 83. 84. 85. 86. 87.]]


我們先練習一下取值的方法,如果想要印出陣列中每一列索引值為1到最後1個的元素,語法為

print(a[::, 1:])

輸出為

[[ 1.  2.  3.  4.  5.  6.  7.]
 [11. 12. 13. 14. 15. 16. 17.]
 [21. 22. 23. 24. 25. 26. 27.]
 [31. 32. 33. 34. 35. 36. 37.]
 [41. 42. 43. 44. 45. 46. 47.]
 [51. 52. 53. 54. 55. 56. 57.]
 [61. 62. 63. 64. 65. 66. 67.]
 [71. 72. 73. 74. 75. 76. 77.]
 [81. 82. 83. 84. 85. 86. 87.]]

如果想要印出陣列中每一列索引值為0的元素,語法為

print(a[::, 0])

輸出為

[ 0. 10. 20. 30. 40. 50. 60. 70. 80.]



向左平移1格


我們先產生一個新的陣列 left,用來儲存平移後的資料,再利用索引值將平移後的元素填入對應的位置,語法為

left = np.zeros(m * n).reshape(m, n)
left[::, 0:-1] = a[::, 1:]
left[::, -1] = a[::, 0]
print(left)

輸出為

[[ 1.  2.  3.  4.  5.  6.  7.  0.]
 [11. 12. 13. 14. 15. 16. 17. 10.]
 [21. 22. 23. 24. 25. 26. 27. 20.]
 [31. 32. 33. 34. 35. 36. 37. 30.]
 [41. 42. 43. 44. 45. 46. 47. 40.]
 [51. 52. 53. 54. 55. 56. 57. 50.]
 [61. 62. 63. 64. 65. 66. 67. 60.]
 [71. 72. 73. 74. 75. 76. 77. 70.]
 [81. 82. 83. 84. 85. 86. 87. 80.]]



向右平移1格


我們先產生一個新的陣列 right,用來儲存平移後的資料,再利用索引值將平移後的元素填入對應的位置,語法為

right = np.zeros(m * n).reshape(m, n)
right[::, 1:] = a[::, 0:-1]
right[::, 0] = a[::, -1]
print(right)

輸出為

[[ 7.  0.  1.  2.  3.  4.  5.  6.]
 [17. 10. 11. 12. 13. 14. 15. 16.]
 [27. 20. 21. 22. 23. 24. 25. 26.]
 [37. 30. 31. 32. 33. 34. 35. 36.]
 [47. 40. 41. 42. 43. 44. 45. 46.]
 [57. 50. 51. 52. 53. 54. 55. 56.]
 [67. 60. 61. 62. 63. 64. 65. 66.]
 [77. 70. 71. 72. 73. 74. 75. 76.]
 [87. 80. 81. 82. 83. 84. 85. 86.]]




向上平移1格


我們先產生一個新的陣列 top,用來儲存平移後的資料,再利用索引值將平移後的元素填入對應的位置,語法為

top = np.zeros(m * n).reshape(m, n)
top[0:-1, ::] = a[1:, ::]
top[-1, ::] = a[0, ::]
print(top)

輸出為

[[10. 11. 12. 13. 14. 15. 16. 17.]
 [20. 21. 22. 23. 24. 25. 26. 27.]
 [30. 31. 32. 33. 34. 35. 36. 37.]
 [40. 41. 42. 43. 44. 45. 46. 47.]
 [50. 51. 52. 53. 54. 55. 56. 57.]
 [60. 61. 62. 63. 64. 65. 66. 67.]
 [70. 71. 72. 73. 74. 75. 76. 77.]
 [80. 81. 82. 83. 84. 85. 86. 87.]
 [ 0.  1.  2.  3.  4.  5.  6.  7.]]



向下平移1格


我們先產生一個新的陣列 bottom,用來儲存平移後的資料,再利用索引值將平移後的元素填入對應的位置,語法為

bottom = np.zeros(m * n).reshape(m, n)
bottom[1:, ::] = a[0:-1, ::]
bottom[0, ::] = a[-1, ::]
print(bottom)

輸出為

[[80. 81. 82. 83. 84. 85. 86. 87.]
 [ 0.  1.  2.  3.  4.  5.  6.  7.]
 [10. 11. 12. 13. 14. 15. 16. 17.]
 [20. 21. 22. 23. 24. 25. 26. 27.]
 [30. 31. 32. 33. 34. 35. 36. 37.]
 [40. 41. 42. 43. 44. 45. 46. 47.]
 [50. 51. 52. 53. 54. 55. 56. 57.]
 [60. 61. 62. 63. 64. 65. 66. 67.]
 [70. 71. 72. 73. 74. 75. 76. 77.]]



同時向左、向上各平移1格


我們先產生一個新的陣列 top_left,用來儲存平移後的資料,再利用索引值將平移後的元素填入對應的位置,語法為

top_left = np.zeros(m * n).reshape(m, n)
top_left[0:-1, 0:-1] = a[1:, 1:] # 將陣列a第1列、第1行到右下角的元素填到top_left的左上角
top_left[-1, -1] = a[0, 0]       # 將陣列a左上角的元素填到top_left的右下角
top_left[-1, 0:-1] = a[0, 1:]    # 將陣列a第0列中除了索引值為1到最後的元素填到top_left的最後1列
top_left[0:-1, -1] = a[1:, 0]    # 將陣列a第1列到最後1列中索引值為0的元素填到top_left的最後1行
print(top_left)

輸出為

[[11. 12. 13. 14. 15. 16. 17. 10.]
 [21. 22. 23. 24. 25. 26. 27. 20.]
 [31. 32. 33. 34. 35. 36. 37. 30.]
 [41. 42. 43. 44. 45. 46. 47. 40.]
 [51. 52. 53. 54. 55. 56. 57. 50.]
 [61. 62. 63. 64. 65. 66. 67. 60.]
 [71. 72. 73. 74. 75. 76. 77. 70.]
 [81. 82. 83. 84. 85. 86. 87. 80.]
 [ 1.  2.  3.  4.  5.  6.  7.  0.]]



方法2:使用 numpy.roll


numpy.roll 的語法為

numpy.roll(陣列, 移動格數, axis = 0 或 1)

若 axis = 0 會縱向移動,若 axis = 1 會橫向移動。詳細的說明請參考官方說明書numpy.roll。以下我們改用 numpy.roll 再做一次平移,由於這樣的寫法與方法1的寫法效果相同,故省略輸出結果。

向左平移1格


left_roll = np.roll(a, -1, axis = 1)
print(left_roll)



向右平移1格


right_roll = np.roll(a, 1, axis = 1)
print(right_roll)



向上平移1格


top_roll = np.roll(a, -1, axis = 0)
print(top_roll)



向下平移1格


bottom_roll = np.roll(a, 1, axis = 0)
print(bottom_roll)



同時向左、向上各平移1格


roll = np.roll(a, -1, axis = 0)
roll = np.roll(roll, -1, axis = 1)
print(roll)



結語

當我聽到這個問題時,第一個想法是用索引值取出元素再存入對應的位置,也就是方法1。但是我覺得 Numpy 可能有已經寫好的工具,果然搜尋一下就找到了 numpy.roll,Numpy 的工具果然很齊全。


HackMD 版本連結:https://hackmd.io/s/SyJxcf9fV

沒有留言:

張貼留言