Chapter 1 Preliminary knowledge
To generate a sequence of numbers, we can write in Python:
L = []
def my_func(x) :
return 2*x
for i in range(5):
L.append(my_func(i))
L
Copy the code
[0, 2, 4, 6, 8]
Copy the code
[* for I in *]. [* for I in *] Where, the first * is a mapping function whose input is what I refers to, and the second * represents the object of the iteration.
[my_func(i) for i in range(5)]
Copy the code
[0, 2, 4, 6, 8]
Copy the code
List expressions also support multiple levels of nesting, with the first for being the outer loop and the second the inner loop in the following example:
[m+'_'+n for m in ['a'.'b'] for n in ['c'.'d']]
Copy the code
['a_c', 'a_d', 'b_c', 'b_d']
Copy the code
In addition to list comprehensions, another useful syntactic sugar is conditional assignment with if selection, of the form value = a if condition else b:
value = 'cat' if 2>1 else 'dog'
value
Copy the code
'cat'
Copy the code
This is equivalent to writing:
a, b = 'cat'.'dog'
condition = 2 > 1 # this is True
if condition:
value = a
else:
value = b
Copy the code
For example, truncate the list of elements over 5, i.e., replace those over 5 with 5, and leave those less than 5 unchanged:
L = [1.2.3.4.5.6.7]
[i if i <= 5 else 5 for i in L]
Copy the code
[1, 2, 3, 4, 5, 5, 5]
Copy the code
2. Anonymous functions and Map methods
Some functions have clear and simple mappings, such as the my_func function above, which can be expressed concisely as anonymous functions:
my_func = lambda x: 2*x
my_func(3)
Copy the code
6
Copy the code
multi_para_func = lambda a, b: a + b
multi_para_func(1.2)
Copy the code
3
Copy the code
In fact, it is often used in situations where multiple calls are not required, such as the example in the list derivation above, where the user does not care about the function name, but only the mapping relationship:
[(lambda x: 2*x)(i) for i in range(5)]
Copy the code
[0, 2, 4, 6, 8]
Copy the code
Python provides a map function that returns a map object that needs to be converted from a list to a list:
list(map(lambda x: 2*x, range(5)))
Copy the code
[0, 2, 4, 6, 8]
Copy the code
Function mapping with multiple input values can be implemented by appending iterators:
list(map(lambda x, y: str(x)+'_'+y, range(5), list('abcde')))
Copy the code
['0_a', '1_b', '2_c', '3_d', '4_e']
Copy the code
3. Zip object and Enumerate method
The zip function can package multiple iterables into a single tuple of iterables. It returns a zip object. The tuple and list can be packaged as follows:
L1, L2, L3 = list('abc'), list('def'), list('hij')
list(zip(L1, L2, L3))
Copy the code
[('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j')]
Copy the code
tuple(zip(L1, L2, L3))
Copy the code
(('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j'))
Copy the code
The zip function is often used when iterating through the loop:
for i, j, k in zip(L1, L2, L3):
print(i, j, k)
Copy the code
a d h
b e i
c f j
Copy the code
Enumerate is a special packing that binds the traversal ordinal of the iterating element during iteration:
L = list('abcd')
for index, value in enumerate(L):
print(index, value)
Copy the code
0 a
1 b
2 c
3 d
Copy the code
This can also be done easily with a ZIP object:
for index, value in zip(range(len(L)), L):
print(index, value)
Copy the code
0 a
1 b
2 c
3 d
Copy the code
When you need to create dictionary mappings between two lists, you can use zip objects:
dict(zip(L1, L2))
Copy the code
{'a': 'd', 'b': 'e', 'c': 'f'}
Copy the code
Now that we have the compression function, Python also provides the * operator to use in conjunction with zip to decompress:
zipped = list(zip(L1, L2, L3))
zipped
Copy the code
[('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j')]
Copy the code
list(zip(*zipped)) The three tuples correspond to the original list
Copy the code
[('a', 'b', 'c'), ('d', 'e', 'f'), ('h', 'i', 'j')]
Copy the code
Numpy fundamentals
1. Construction of NP array
The most common method is to construct an array:
import numpy as np
np.array([1.2.3])
Copy the code
array([1, 2, 3])
Copy the code
Here’s how to generate some special arrays:
[a] Arithmetic sequence: NP. linspace, NP. arange
np.linspace(1.5.11) # Start, stop (include), number of samples
Copy the code
Array ([1., 1.4, 1.8, 2.2, 2.6, 3., 3.4, 3.8, 4.2, 4.6, 5.])Copy the code
np.arange(1.5.2) # Start, end (not included), step size
Copy the code
array([1, 3])
Copy the code
[b] Special matrix: Zeros, Eye, full
np.zeros((2.3)) A tuple is passed to indicate the size of each dimension
Copy the code
array([[0., 0., 0.],
[0., 0., 0.]])
Copy the code
np.eye(3) # 3 by 3 identity matrix
Copy the code
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
Copy the code
np.eye(3, k=1) Offset the main diagonal by 1 unit to the pseudo-identity matrix
Copy the code
array([[0., 1., 0.],
[0., 0., 1.],
[0., 0., 0.]])
Copy the code
np.full((2.3), 10) The tuple is passed in size, with 10 representing the padding value
Copy the code
array([[10, 10, 10],
[10, 10, 10]])
Copy the code
np.full((2.3),1.2.3]) Fill in the same list for each line
Copy the code
array([[1, 2, 3],
[1, 2, 3]])
Copy the code
[c] Random matrix: NP. Random
The most commonly used random generating functions are RAND, RANDn, RANDINT, and choice, which represent 0-1 uniformly distributed random number group, standard normal random number group, random integer group, and random list sampling respectively:
np.random.rand(3) Generate three random numbers that follow a uniform distribution of 0-1
Copy the code
Array ([0.92340835, 0.20019461, 0.40755472])Copy the code
np.random.rand(3.3) Note that this is not a tuple; each dimension size is entered separately
Copy the code
Array ([[0.8012362, 0.53154881, 0.05858554], [0.13103034, 0.18108091, 0.30253153], [0.00528884, 0.99402007, 0.36348797]])Copy the code
For the uniform distribution obeying the interval a to B, it can be generated as follows:
a, b = 5.15
(b - a) * np.random.rand(3) + a
Copy the code
Array ([6.59370831, 8.03865138, 9.19172546])Copy the code
In general, you can select an existing library function:
np.random.uniform(5.15.3)
Copy the code
Array ([11.26499636, 13.12311185, 6.00774156])Copy the code
Randn generates the standard normal distribution of N(0,I) :
np.random.randn(3)
Copy the code
Array ([1.87000209, 1.19885561, 0.58802943])Copy the code
np.random.randn(2.2)
Copy the code
Array ([[1.3642839, 0.31497567], [1.9452492, 3.17272882]])Copy the code
A unary normal distribution with variance σ2\sigma^2 and mean σ2 μ\muμ can be generated as follows:
sigma, mu = 2.5.3
mu + np.random.randn(3) * sigma
Copy the code
Array ([1.56024917, 0.22829486, 7.3764211])Copy the code
Alternatively, it can be generated from an existing function:
np.random.normal(3.2.5.3)
Copy the code
Array ([3.53517851, 5.3441269, 3.51192744])Copy the code
Randint can specify the minimum maximum value (not included) and dimension size for generating random integers:
low, high, size = 5.15, (2.2) Generate random integers from 5 to 14
np.random.randint(low, high, size)
Copy the code
array([[ 5, 12],
[14, 9]])
Copy the code
Choice can extract results from a given list with a certain probability and method. If the probability is not specified, uniform sampling is used. The default extraction method is put back sampling:
my_list = ['a'.'b'.'c'.'d']
np.random.choice(my_list, 2, replace=False, p=[0.1.0.7.0.1 ,0.1])
Copy the code
array(['b', 'a'], dtype='<U1')
Copy the code
np.random.choice(my_list, (3.3))
Copy the code
array([['c', 'b', 'd'],
['d', 'a', 'd'],
['a', 'c', 'd']], dtype='<U1')
Copy the code
When the number of elements returned is the same as the original list, not putting back the sample is equivalent to using the permutation function, that is, smashing the original list:
np.random.permutation(my_list)
Copy the code
array(['c', 'a', 'd', 'b'], dtype='<U1')
Copy the code
Finally, we need to mention the random seed, which can fix the output of random numbers:
np.random.seed(0)
np.random.rand()
Copy the code
0.5488135039273248
Copy the code
np.random.seed(0)
np.random.rand()
Copy the code
0.5488135039273248
Copy the code
2. Np array deformation and merge
[a] transpose: T
np.zeros((2.3)).T
Copy the code
array([[0., 0.],
[0., 0.],
[0., 0.]])
Copy the code
[b] Merge operation: r_, c_
For two-dimensional arrays, r_ and c_ represent up-down and left-right merges, respectively:
np.r_[np.zeros((2.3)),np.zeros((2.3)))Copy the code
array([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
Copy the code
np.c_[np.zeros((2.3)),np.zeros((2.3)))Copy the code
array([[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.]])
Copy the code
When combining one-dimensional and two-dimensional arrays, we should treat them as column vectors, using only the left and right merge c_ operation in case of length matching:
try:
np.r_[np.array([0.0]),np.zeros((2.1)))except Exception as e:
Err_Msg = e
Err_Msg
Copy the code
ValueError('all the input arrays must have same number of dimensions, but the array at index 0 has 1 dimension(s) and the array at index 1 has 2 dimension(s)')
Copy the code
np.r_[np.array([0.0]),np.zeros(2)]
Copy the code
array([0., 0., 0., 0.])
Copy the code
np.c_[np.array([0.0]),np.zeros((2.3)))Copy the code
array([[0., 0., 0., 0.],
[0., 0., 0., 0.]])
Copy the code
【 C 】 Dimension change: 0
0 0 Is helping users rearrange the array into new dimensions In use, there are two modes, respectively C mode and F mode, which are filled in row by row and column by column, respectively.
target = np.arange(8).reshape(2.4)
target
Copy the code
array([[0, 1, 2, 3],
[4, 5, 6, 7]])
Copy the code
target.reshape((4.2), order='C') Read and populate by line
Copy the code
array([[0, 1],
[2, 3],
[4, 5],
[6, 7]])
Copy the code
target.reshape((4.2), order='F') Read and populate by column
Copy the code
array([[0, 2],
[4, 6],
[1, 3],
[5, 7]])
Copy the code
In particular, since the size of the array being called is definite, 0 Is 0 0 Allows one dimension to be empty, and is 0 0 0 Just padding -1:
target.reshape((4, -1))
Copy the code
array([[0, 1],
[2, 3],
[4, 5],
[6, 7]])
Copy the code
The following operations to convert an n*1 array to a 1-dimensional array are commonly used:
target = np.ones((3.1))
target
Copy the code
array([[1.],
[1.],
[1.]])
Copy the code
target.reshape(-1)
Copy the code
array([1., 1., 1.])
Copy the code
3. Slice and index of NP array
The slicing mode of arrays supports slicing using the start:end:step slicing of type slice, or directly passing in the index of a specified dimension to the list for slicing:
target = np.arange(9).reshape(3.3)
target
Copy the code
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
Copy the code
target[:-1[0.2]]
Copy the code
array([[0, 2],
[3, 5]])
Copy the code
In addition, np.ix_ can be used to use Boolean indexes on the corresponding dimensions, but slice cannot be used at this time:
target[np.ix_([True.False.True], [True.False.True]]Copy the code
array([[0, 2],
[6, 8]])
Copy the code
target[np.ix_([1.2], [True.False.True]]Copy the code
array([[3, 5],
[6, 8]])
Copy the code
When the array dimension is 1, Boolean indexing can be done directly without np.ix_ :
new = target.reshape(-1)
new[new%2= =0]
Copy the code
array([0, 2, 4, 6, 8])
Copy the code
4. Common functions
For simplicity, the following functions are assumed to have one-dimensional arrays of inputs.
【 a 】 the where
Where is a conditional function that can specify fillers for positions that satisfy conditions and those that do not:
a = np.array([-1.1, -1.0])
np.where(a>0, a, 5) Fill a if position is True, otherwise fill 5
Copy the code
array([5, 1, 5, 5])
Copy the code
【 B 】 Nonzero, argmax, argmin
Nonzero returns a non-zero index, argmax and argmin return the maximum and minimum index, respectively:
a = np.array([-2, -5.0.1.3, -1])
np.nonzero(a)
Copy the code
(array([0, 1, 3, 4, 5], dtype=int64),)
Copy the code
a.argmax()
Copy the code
4
Copy the code
a.argmin()
Copy the code
1
Copy the code
【 C 】any, all
Any means to return True if the sequence has at least one True or non-zero element, and False otherwise
All: Returns True if all of the sequence elements are True or non-zero, and False otherwise
a = np.array([0.1])
a.any(a)Copy the code
True
Copy the code
a.all(a)Copy the code
False
Copy the code
【 D 】cumprod
Cumprod, cumsum respectively, multiplicative and additive function returns the length of the array, and diff said before an element to do bad, due to the first element for the missing value, so in the case of the default parameters, return minus 1 is the original array length
a = np.array([1.2.3])
a.cumprod()
Copy the code
array([1, 2, 6], dtype=int32)
Copy the code
a.cumsum()
Copy the code
array([1, 3, 6], dtype=int32)
Copy the code
np.diff(a)
Copy the code
array([1, 1])
Copy the code
[e] Statistical function
The commonly used statistical functions include Max, min, mean, median, STD, var, sum, and quantile, where quantile calculation is a global method and therefore cannot be called by the array.quantile method:
target = np.arange(5)
target
Copy the code
array([0, 1, 2, 3, 4])
Copy the code
target.max(a)Copy the code
4
Copy the code
np.quantile(target, 0.5) # 0.5 quantile
Copy the code
2.0
Copy the code
Arrays with missing values, however, return missing values as well. To skip missing values, you must use nan* functions, and several of the statistical functions above have nan* functions.
target = np.array([1.2, np.nan])
target
Copy the code
array([ 1., 2., nan])
Copy the code
target.max(a)Copy the code
nan
Copy the code
np.nanmax(target)
Copy the code
2.0
Copy the code
np.nanquantile(target, 0.5)
Copy the code
1.5
Copy the code
Cov can be used for covariance and correlation coefficient respectively, and CORrCOEF can be calculated as follows:
target1 = np.array([1.3.5.9])
target2 = np.array([1.5.3, -9])
np.cov(target1, target2)
Copy the code
Array ([[11.66666667, -16.66666667], [-16.66666667, 38.66666667]])Copy the code
np.corrcoef(target1, target2)
Copy the code
Array ([[1., -0.78470603], [-0.78470603, 1.]])Copy the code
Finally, it is necessary to describe the axis parameter of the statistical function in the two-dimensional Numpy array, which can perform statistical feature calculation in a certain dimension. When Axis =0, the result is the statistical index of the column, and when Axis =1, the result is the statistical index of the row:
target = np.arange(1.10).reshape(3, -1)
target
Copy the code
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
Copy the code
target.sum(0)
Copy the code
array([12, 15, 18])
Copy the code
target.sum(1)
Copy the code
array([ 6, 15, 24])
Copy the code
5. Broadcast mechanism
The broadcast mechanism is used to handle operations between two arrays of different dimensions, and only arrays of up to two dimensions are discussed here.
[a] Scalar and array operations
When a scalar evaluates to an array, the scalar automatically expands to the array size, and then performs element-by-element operations:
res = 3 * np.ones((2.2)) + 1
res
Copy the code
array([[4., 4.],
[4., 4.]])
Copy the code
res = 1 / res
res
Copy the code
Array ([[0.25, 0.25], [0.25, 0.25]])Copy the code
[b] Operations between two-dimensional arrays
If two array dimensions are exactly the same, an error will be reported unless one of the dimensions is M ×1m×1m×1 or 1× N1 × N1 × N, then the dimension with 111 will be enlarged to the corresponding dimension of the other array. For example, the 1×21×21×2 and 3×23×23×2 arrays will expand the first array to 3×23×23×2 element by element, and the corresponding value of the expansion will be assigned. Note, however, that if the dimensions of the first array are 1×31×31×3, then an error is reported because the sizes in the second dimension do not match and are not 111.
res = np.ones((3.2))
res
Copy the code
array([[1., 1.],
[1., 1.],
[1., 1.]])
Copy the code
res * np.array([[2.3]]) The second array expands the first dimension to 3
Copy the code
array([[2., 3.],
[2., 3.],
[2., 3.]])
Copy the code
res * np.array([[2], [3], [4]]) The second array extends the second dimension by 2
Copy the code
array([[2., 2.],
[3., 3.],
[4., 4.]])
Copy the code
res * np.array([[2]]) The second array has two dimensions expanded to 3 and 2 respectively
Copy the code
array([[2., 2.],
[2., 2.],
[2., 2.]])
Copy the code
[c] The operation of one-dimensional and two-dimensional arrays
When the one-dimensional array AkA_kAk operates with the two-dimensional array Bm,nB_{m,n}Bm,n, it is equivalent to treat the one-dimensional array as the two-dimensional array A1,kA_{1,k}A1, K, using the same broadcast law as in [b], when k! =nk! =nk! =n and k,nk,nk,n are not 111.
np.ones(3) + np.ones((2.3))
Copy the code
array([[2., 2., 2.],
[2., 2., 2.]])
Copy the code
np.ones(3) + np.ones((2.1))
Copy the code
array([[2., 2., 2.],
[2., 2., 2.]])
Copy the code
np.ones(1) + np.ones((2.3))
Copy the code
array([[2., 2., 2.],
[2., 2., 2.]])
Copy the code
6. Calculation of vectors and matrices
【a】 Vector inner product: dot
a = np.array([1.2.3])
b = np.array([1.3.5])
a.dot(b)
Copy the code
22
Copy the code
[b] Vector norm and matrix norm: Np.linalg. norm
In the calculation of matrix norm, the most important parameter is the ORD parameter, which can have the following values:
ord | norm for matrices | norm for vectors |
---|---|---|
None | Frobenius norm | 2-norm |
‘fro’ | Frobenius norm | / |
‘nuc’ | nuclear norm | / |
inf | max(sum(abs(x), axis=1)) | max(abs(x)) |
-inf | min(sum(abs(x), axis=1)) | min(abs(x)) |
0 | / | sum(x ! = 0) |
1 | max(sum(abs(x), axis=0)) | as below |
– 1 | min(sum(abs(x), axis=0)) | as below |
2 | 2-norm (largest sing. value) | as below |
2 – | smallest singular value | as below |
other | / | sum(abs(x)**ord)**(1./ord) |
matrix_target = np.arange(4).reshape(-1.2)
matrix_target
Copy the code
array([[0, 1],
[2, 3]])
Copy the code
np.linalg.norm(matrix_target, 'fro')
Copy the code
3.7416573867739413
Copy the code
np.linalg.norm(matrix_target, np.inf)
Copy the code
5.0
Copy the code
np.linalg.norm(matrix_target, 2)
Copy the code
3.702459173643833
Copy the code
vector_target = np.arange(4)
vector_target
Copy the code
array([0, 1, 2, 3])
Copy the code
np.linalg.norm(vector_target, np.inf)
Copy the code
3.0
Copy the code
np.linalg.norm(vector_target, 2)
Copy the code
3.7416573867739413
Copy the code
np.linalg.norm(vector_target, 3)
Copy the code
3.3019272488946263
Copy the code
【 C 】 Matrix multiplication: @
a = np.arange(4).reshape(-1.2)
a
Copy the code
array([[0, 1],
[2, 3]])
Copy the code
b = np.arange(-4.0).reshape(-1.2)
b
Copy the code
array([[-4, -3],
[-2, -1]])
Copy the code
a@b
Copy the code
array([[ -2, -1],
[-14, -9]])
Copy the code
Three, practice
Ex1: Use list derivation to write matrix multiplication
The general matrix multiplication can be written by a triple loop according to the formula, please rewrite it as a list derivation.
M1 = np.random.rand(2.3)
M2 = np.random.rand(3.4)
res = np.empty((M1.shape[0],M2.shape[1]))
for i in range(M1.shape[0) :for j in range(M2.shape[1]):
item = 0
for k in range(M1.shape[1]):
item += M1[i][k] * M2[k][j]
res[i][j] = item
(np.abs((M1@M2 - res) < 1e-15)).all(a)# Eliminate numerical errors
Copy the code
True
Copy the code
Ex2: update matrix
Let the matrix Am×nA_{m×n}Am×n, now update each element in AAA to generate the matrix BBB, Bij=Aij∑k=1n1AikB_{ij}=A_{ij}\sum_{k=1}^n\frac{1}{A_{ik}}Bij=Aij∑k=1nAik1, for example, the matrix is AAA, The B2, 2 = 5 x (14 + 15, 16) = 3712 b_ {2} = 5 \ times (\ frac {1} {4} + \ frac {1} {5} + \ frac {1} {6}) = \ frac {37} {12} B2, 2 = 5 x (41 + 51 + 61) = 1237, Use Numpy for efficient implementation. \begin{split}A=\left[ \begin{matrix} 1 & 2 &3\\4&5&6\\7&8&9 \end{matrix} \right]\end{split}
Ex3: Chi-square statistics
Let the matrix Am×nA_{m\times n}Am×n, Remember Bij = maij (∑ I = 1) x (∑ j = 1 naij) ∑ ∑ I = 1 m j = 1 naijb_ = {ij} \ frac {(\ sum_ {I = 1} ^ mA_ {ij}) \ times (\ sum_ {j = 1} ^ nA_ {ij})} {\ sum_ {I = 1} ^ m \ sum_ {j = 1} ^ nA_ {ij}} Bij = ∑ ∑ I = 1 m j = 1 naij maij (∑ I = 1), x (∑ j = 1 naij), define the chi-square value is as follows: Chi-square = ∑ ∑ I = 1 m j = 1 n (Aij – Bij) Bij \ chi ^ 2 = \ sum_ {I = 1} ^ m \ sum_ {j = 1} ^ n \ frac {(A_ {ij} – B_ {ij}) ^ 2} {B_ {ij}} chi-square = ∑ ∑ I = 1 m j = 1 nbij (Aij – Bij) 2 Calculate χ2\chi^2χ2 for a given matrix AAA using Numpy
np.random.seed(0)
A = np.random.randint(10.20, (8.5))
Copy the code
Ex4: Improve the performance of matrix calculation
Let ZZZ be m×nm×nm× N matrix, BBB and UUU are m× PM × PM × P and P × NP × NP × N matrix respectively, BiB_iBi is the third row of BBB, UjU_jUj is the JJJ column of UUU, Defined below R = ∑ ∑ I = 1 m j = 1 n ∥ Bi – Uj ∥ 22 zij \ displaystyle R = \ sum_ {I = 1} ^ m \ sum_ {j = 1} ^ n \ | B_i – U_j \ | _2 ^ 2 z_ {ij} ∑ ∑ mj = 1 R = I = 1 n ∥ Bi – Uj ∥ 22 zij. A of ∥ ∥ 22 \ | \ mathbf {a} \ | _2 ^ 2 ∥ ∥ 22 said a vector aaa weight sum of squares of ∑ iai2 \ sum_i a_i ^ 2 ∑ iai2.
Now that someone has calculated the value of RRR from the sample data given below, take full advantage of the functions in Numpy to improve the performance of this code based on the problem.
np.random.seed(0)
m, n, p = 100.80.50
B = np.random.randint(0.2, (m, p))
U = np.random.randint(0.2, (p, n))
Z = np.random.randint(0.2, (m, n))
def solution(B=B, U=U, Z=Z) :
L_res = []
for i in range(m):
for j in range(n):
norm_value = ((B[i]-U[:,j])**2).sum()
L_res.append(norm_value*Z[i][j])
return sum(L_res)
solution(B, U, Z)
Copy the code
100566
Copy the code
Ex5: indicates the maximum length of consecutive integers
Enter a Numpy array of integers and return the maximum length of a subarray of strictly incrementing consecutive integers. Forward is the incrementing direction. For example, the inputs [1,2,5,6,7], [5,6,7] are subarrays of contiguous integers with maximum length, so the output is 3; The inputs [3,2,1,2,3,4,6], [1,2,3,4] are subarrays of contiguous integers with the maximum length, so the output is 4. Take full advantage of Numpy’s built-in functions to do this. (Tip: Consider using the Nonzero, diff function.)