Array copy versus Array View
When you .copy() an array into another array, it will be its own distinct space in memory with a control of its copy of the data. However, if you run the .view() member function on an array, any change to the view changes the original and vice versa. Think of .view() as having a memory address reference to an established variable instead of a new variable.
If you're uncertain whether a variable is a copy, or a view of the original, you can use the .base member property of the array class to figure it out. It will return None if it's a copy, and the actual original full array if not.
Use the .shape member property of the array class to return a tuple of the length of each dimension.
A somewhat complex and feature-rich member method of the array object class is the .reshape() function. In order to reshape an array, you cannot have any voids or unused elements, which means the multiplicative sum of the dimensions in the original array must equal the multiplicative sum of the dimensions for the reshaped array. Even after you do a reshape, checking the .base member property will turn the original array, which means it must be using extra memory if it's able to know the original array and also the reshaped one.
For example:
[[1, 2, 3], [7,8,9]]
is a 2 (rows) by 3 (columns) dimensional array. It is convertible into a 3 by 2 array or a 1 by 6 array or into a 6 by 1 array. That's because 2*3 = 3*2 = 1*6 = 6*1 = 6. It is NOT convertible into 3 by 3 array because 3*3= = 9, so there would be 3 void/empty positions in the 3 by 3 array; Python will generate an error message if you try this.
ValueError: cannot reshape array of size 6 into shape (3,3)
import numpy as np
hopscotch = np.array([[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]]) # 2 rows by 6 columns
print(hopscotch)
new_hopscotch = hopscotch.reshape(4, 3)
print(new_hopscotch)
# results in a second array with 4 rows and 3 columns
[[ 1 2 3 4 5 6]
[ 7 8 9 10 11 12]]
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
You can also reshape two arrays of the same dimensionality for example a 2-D array of 3 by 5, into a 2-D array with 5 by 3 elements preserves all elements without voids or unused elements. Both arrays are two dimensional; you just transposed them.
Python allows you to have one unknown dimension in a .reshape() function call. The unknown dimension is marked by -1 and it can be in any position of any dimension of the argument list of the .reshape() function call.
If you do reshape with a single parameter of -1, that will flatten any N-dimensional array into a 1D array.
import numpy as np
hopscotch = np.array([1, 2, 3, 4, 5, 6, 7, 8])
new_hopscotch = hopscotch.reshape(2, -1, 2)
# .reshape() figures out on its own what the 2nd dimension length should be.
print(new_hopscotch)
print(new_hopscotch.base)
# results in:
[[[1 2]
[3 4]]
[[5 6]
[7 8]]]
[1 2 3 4 5 6 7 8]
--------------------------------
import numpy as np
original = np.array([[1, 2, 3], [4, 5, 6]])
for x in original:
for y in x:
print(y)
print("------ The next version may be faster as it only uses one loop ------------")
newone = np.array([[1, 2, 3], [4, 5, 6]]).reshape(-1)
for x in newone:
print(x)
There's a more powerful and flexible version of reshape with the -1 parameter, and it's the .nditer() member method of the array class. It can not only reshape and flatten, but it can take additional optional arguments, namely to alter the data types of the array elements.
import numpy as np
numbers = np.array([1, 2, 3])
for x in np.nditer(numbers, flags=['buffered'], op_dtypes=['S']):
print(x)
Finally, the .ndenumerate() method of the array class returns element values and the index associated with each element value. If the array is multi-dimensional, it will give you multi-dimensional indices. If you're going to create a for loop to access each element separately, use two dummy variables: one for the index and one for the element value. Remember that arrays start counting at 0, so .ndenumerate() will return indices starting at 0.
import numpy as np
square1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]]) # 2D
for iy, y in np.ndenumerate(square1):
print(iy, y)
print("------------------")
square2 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]]).reshape(-1) # 1D
for iz, z in np.ndenumerate(square2):
print(iz, z)
# results in: - the numbers in parentheses are array indices
(0, 0) 1
(0, 1) 2
(0, 2) 3
(0, 3) 4
(1, 0) 5
(1, 1) 6
(1, 2) 7
(1, 3) 8
------------------
(0,) 1
(1,) 2
(2,) 3
(3,) 4
(4,) 5
(5,) 6
(6,) 7
(7,) 8
Both .nditer() and .ndenumerate() respect array slicing syntax.