merge intervals without using loops and classic python code - python

my problem is to merge intervals where I have the overlapping
example:
input:
[(4,8),(6,10),(11,12),(15,20),(20,25)]
output:
[(4, 10),(11,12), (15, 25)]
input:
([(4,8),(6,10),(11,12),(15,20)])
output:
[(4, 10),(11,12), (15, 20)]
I did it with classic python code(using loops, if conditions)
BUT I want to do it with python libraries (pandas, numpy..) in few lines
is there any suggestions?
Thanks in advance

Assuming that your input tuples are sorted like in the examples, something like this does the job:
p = [(4, 8), (6, 10), (11, 12), (15, 20), (20, 25)]
ind = np.where(np.diff(np.array(p).flatten()) <= 0)[0]
np.delete(p, [ind, ind+1]).reshape(-1, 2)
output:
array([[ 4, 10],
[11, 12],
[15, 25]])
Then you can convert it to [(4, 10), (11, 12), (15, 25)] using e.g. list(map(tuple, ...)).
Edit: the above works only if each tuple (x_i, y_i) is such that x_i <= x_{i+1} and y_i <= y_{i+1} for all i's, as in the original examples.
To make it work with the only condition x_i <= y_i for all i, you have to preprocess the list:
# Example from comments (modified by subtracting the min value and removing duplicates)
p = [(0, 90), (72, 81), (87, 108), (459, 606)]
p = list(zip(sorted([tup[0] for tup in p]), sorted([tup[1] for tup in p])))
ind = np.where(np.diff(np.array(p).flatten()) <= 0)[0]
ind = ind[ind % 2 == 1] # this is needed for cases when x_i = y_i
np.delete(p, [ind, ind+1]).reshape(-1, 2)
Output:
array([[ 0, 108],
[459, 606]])

I'm not sure this is necessarily the best option, since it takes O(max_interval_value * num_intervals) time and memory, but this is a straightforward implementation with NumPy:
import numpy as np
def simplify_intervals(intervals):
# Make intervals into an array
intervals = np.asarray(intervals)
# Make array for zero to the greatest interval end (plus bounds values
r = np.arange(intervals[:, 1].max() + 3)[:, np.newaxis]
# Check what elements of the array are within each interval
m = (r >= intervals[:, 0] + 1) & (r <= intervals[:, 1] + 1)
# Collapse belonging test for each value
ind = m.any(1).astype(np.int8)
# Find where the belonging test changes
d = np.diff(ind)
# Find interval bounds
start = np.where(d > 0)[0]
end = np.where(d < 0)[0] - 1
# Make final intervals array
return np.stack((start, end), axis=1)
print(simplify_intervals([(4, 8), (6, 10), (11, 12), (15, 20), (20, 25)]))
# [[ 4 12]
# [15 25]]
print(simplify_intervals(([(4,8),(6,10),(11,12),(15,20)])))
# [[ 4 12]
# [15 20]]
Note: This assumes positive interval values. It could be adapted to support negative ranges, and actually optimized a bit to only consider values from the smallest one to the largest one.
EDIT:
If you want to use this method for large number of intervals or bounds, you may benefit from using Numba instead:
import numpy as np
import numba as nb
#nb.njit
def simplify_intervals_nb(intervals):
n = 0
for _, end in intervals:
n = max(n, end)
r = np.arange(n + 3)
m = np.zeros(n + 3, dtype=np.bool_)
for start, end in intervals:
m |= (r >= start + 1) & (r <= end + 1)
ind = m.astype(np.int8)
# Find where the belonging test changes
d = np.diff(ind)
# Find interval bounds
start = np.where(d > 0)[0]
end = np.where(d < 0)[0] - 1
# Make final intervals array
return np.stack((start, end), axis=1)
Quick test in IPython:
import random
random.seed(100)
start = [random.randint(0, 10000) for _ in range(300)]
end = start = [s + random.randint(0, 3000) for s in start]
intervals = list(zip(start, end))
print(np.all(simplify_intervals(intervals) == simplify_intervals_nb(intervals)))
# True
%timeit simplify_intervals(intervals)
# 15.2 ms ± 179 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit simplify_intervals_nb(intervals)
# 9.54 ms ± 146 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Related

combination of numpy rows [duplicate]

I would like to implement itertools.combinations for numpy. Based on this discussion, I have a function that works for 1D input:
def combs(a, r):
"""
Return successive r-length combinations of elements in the array a.
Should produce the same output as array(list(combinations(a, r))), but
faster.
"""
a = asarray(a)
dt = dtype([('', a.dtype)]*r)
b = fromiter(combinations(a, r), dt)
return b.view(a.dtype).reshape(-1, r)
and the output makes sense:
In [1]: list(combinations([1,2,3], 2))
Out[1]: [(1, 2), (1, 3), (2, 3)]
In [2]: array(list(combinations([1,2,3], 2)))
Out[2]:
array([[1, 2],
[1, 3],
[2, 3]])
In [3]: combs([1,2,3], 2)
Out[3]:
array([[1, 2],
[1, 3],
[2, 3]])
However, it would be best if I could expand it to N-D inputs, where additional dimensions simply allow you to speedily do multiple calls at once. So, conceptually, if combs([1, 2, 3], 2) produces [1, 2], [1, 3], [2, 3], and combs([4, 5, 6], 2) produces [4, 5], [4, 6], [5, 6], then combs((1,2,3) and (4,5,6), 2) should produce [1, 2], [1, 3], [2, 3] and [4, 5], [4, 6], [5, 6] where "and" just represents parallel rows or columns (whichever makes sense). (and likewise for additional dimensions)
I'm not sure:
How to make the dimensions work in a logical way that's consistent with the way other functions work (like how some numpy functions have an axis= parameter, and a default of axis 0. So probably axis 0 should be the one I am combining along, and all other axes just represent parallel calculations?)
How to get the above code to work with ND (right now I get ValueError: setting an array element with a sequence.)
Is there a better way to do dt = dtype([('', a.dtype)]*r)?
You can use itertools.combinations() to create the index array, and then use NumPy's fancy indexing:
import numpy as np
from itertools import combinations, chain
from scipy.special import comb
def comb_index(n, k):
count = comb(n, k, exact=True)
index = np.fromiter(chain.from_iterable(combinations(range(n), k)),
int, count=count*k)
return index.reshape(-1, k)
data = np.array([[1,2,3,4,5],[10,11,12,13,14]])
idx = comb_index(5, 3)
print(data[:, idx])
output:
[[[ 1 2 3]
[ 1 2 4]
[ 1 2 5]
[ 1 3 4]
[ 1 3 5]
[ 1 4 5]
[ 2 3 4]
[ 2 3 5]
[ 2 4 5]
[ 3 4 5]]
[[10 11 12]
[10 11 13]
[10 11 14]
[10 12 13]
[10 12 14]
[10 13 14]
[11 12 13]
[11 12 14]
[11 13 14]
[12 13 14]]]
Case k = 2: np.triu_indices
I've tested case k = 2 using lots of variations of abovementioned functions using perfplot. The winner is, no doubt, np.triu_indices and I see now that using np.dtype([('', np.intp)] * 2) data structure can be a huge boost even for exotic data types such as igraph.EdgeList.
from itertools import combinations, chain
from scipy.special import comb
import igraph as ig #graph library build on C
import networkx as nx #graph library, pure Python
def _combs(n):
return np.array(list(combinations(range(n),2)))
def _combs_fromiter(n): ##Jaime
indices = np.arange(n)
dt = np.dtype([('', np.intp)]*2)
indices = np.fromiter(combinations(indices, 2), dt)
indices = indices.view(np.intp).reshape(-1, 2)
return indices
def _combs_fromiterplus(n):
dt = np.dtype([('', np.intp)]*2)
indices = np.fromiter(combinations(range(n), 2), dt)
indices = indices.view(np.intp).reshape(-1, 2)
return indices
def _numpy(n): ##endolith
return np.transpose(np.triu_indices(n,1))
def _igraph(n):
return np.array(ig.Graph(n).complementer(False).get_edgelist())
def _igraph_fromiter(n):
dt = np.dtype([('', np.intp)]*2)
indices = np.fromiter(ig.Graph(n).complementer(False).get_edgelist(), dt)
indices = indices.view(np.intp).reshape(-1, 2)
return indices
def _nx(n):
G = nx.Graph()
G.add_nodes_from(range(n))
return np.array(list(nx.complement(G).edges))
def _nx_fromiter(n):
G = nx.Graph()
G.add_nodes_from(range(n))
dt = np.dtype([('', np.intp)]*2)
indices = np.fromiter(nx.complement(G).edges, dt)
indices = indices.view(np.intp).reshape(-1, 2)
return indices
def _comb_index(n): ##HYRY
count = comb(n, 2, exact=True)
index = np.fromiter(chain.from_iterable(combinations(range(n), 2)),
int, count=count*2)
return index.reshape(-1, 2)
fig = plt.figure(figsize=(15, 10))
plt.grid(True, which="both")
out = perfplot.bench(
setup = lambda x: x,
kernels = [_numpy, _combs, _combs_fromiter, _combs_fromiterplus,
_comb_index, _igraph, _igraph_fromiter, _nx, _nx_fromiter],
n_range = [2 ** k for k in range(12)],
xlabel = 'combinations(n, 2)',
title = 'testing combinations',
show_progress = False,
equality_check = False)
out.show()
Wondering why np.triu_indices can't be extended to more dimensions?
Case 2 ≤ k ≤ 4: triu_indices(implemented here) = up to 2x speedup
np.triu_indices could actually be a winner for case k = 3 and even k = 4 if we implement a generalised method instead. A current version of this method is equivalent of:
def triu_indices(n, k):
x = np.less.outer(np.arange(n), np.arange(-k+1, n-k+1))
return np.nonzero(x)
It constructs matrix representation of a relation x < y for two sequences 0,1,...,n-1 and finds locations of cells where they are not zero. For 3D case we need to add extra dimension and intersect relations x < y and y < z. For next dimensions procedure is the same but this gets a huge memory overload since n^k binary cells are needed and only C(n, k) of them attains True values. Memory usage and performance grows by O(n!) so this algorithm outperformans itertools.combinations only for small values of k. This is best to use actually for case k=2 and k=3
def C(n, k): #huge memory overload...
if k==0:
return np.array([])
if k==1:
return np.arange(1,n+1)
elif k==2:
return np.less.outer(np.arange(n), np.arange(n))
else:
x = C(n, k-1)
X = np.repeat(x[None, :, :], len(x), axis=0)
Y = np.repeat(x[:, :, None], len(x), axis=2)
return X&Y
def C_indices(n, k):
return np.transpose(np.nonzero(C(n,k)))
Let's checkout with perfplot:
import matplotlib.pyplot as plt
import numpy as np
import perfplot
from itertools import chain, combinations
from scipy.special import comb
def C(n, k): # huge memory overload...
if k == 0:
return np.array([])
if k == 1:
return np.arange(1, n + 1)
elif k == 2:
return np.less.outer(np.arange(n), np.arange(n))
else:
x = C(n, k - 1)
X = np.repeat(x[None, :, :], len(x), axis=0)
Y = np.repeat(x[:, :, None], len(x), axis=2)
return X & Y
def C_indices(data):
n, k = data
return np.transpose(np.nonzero(C(n, k)))
def comb_index(data):
n, k = data
count = comb(n, k, exact=True)
index = np.fromiter(chain.from_iterable(combinations(range(n), k)),
int, count=count * k)
return index.reshape(-1, k)
def build_args(k):
return {'setup': lambda x: (x, k),
'kernels': [comb_index, C_indices],
'n_range': [2 ** x for x in range(2, {2: 10, 3:10, 4:7, 5:6}[k])],
'xlabel': f'N',
'title': f'test of case C(N,{k})',
'show_progress': True,
'equality_check': lambda x, y: np.array_equal(x, y)}
outs = [perfplot.bench(**build_args(n)) for n in (2, 3, 4, 5)]
fig = plt.figure(figsize=(20, 20))
for i in range(len(outs)):
ax = fig.add_subplot(2, 2, i + 1)
ax.grid(True, which="both")
outs[i].plot()
plt.show()
So the best performance boost is achieved for k=2 (equivalent to np.triu_indices) and for k=3` it's faster almost twice.
Case k > 3: numpy_combinations(implemented here) = up to 2.5x speedup
Following this question (thanks #Divakar) I managed to find a way to calculate values of specific column based on previous column and Pascal's triangle. It's not optimized yet as much as it could but results are really promising. Here we go:
from scipy.linalg import pascal
def stretch(a, k):
l = a.sum()+len(a)*(-k)
out = np.full(l, -1, dtype=int)
out[0] = a[0]-1
idx = (a-k).cumsum()[:-1]
out[idx] = a[1:]-1-k
return out.cumsum()
def numpy_combinations(n, k):
#n, k = data #benchmark version
n, k = data
x = np.array([n])
P = pascal(n).astype(int)
C = []
for b in range(k-1,-1,-1):
x = stretch(x, b)
r = P[b][x - b]
C.append(np.repeat(x, r))
return n - 1 - np.array(C).T
And the benchmark results are:
# script is the same as in previous example except this part
def build_args(k):
return {'setup': lambda x: (k, x),
'kernels': [comb_index, numpy_combinations],
'n_range': [x for x in range(1, k)],
'xlabel': f'N',
'title': f'test of case C({k}, k)',
'show_progress': True,
'equality_check': False}
outs = [perfplot.bench(**build_args(n)) for n in (12, 15, 17, 23, 25, 28)]
fig = plt.figure(figsize=(20, 20))
for i in range(len(outs)):
ax = fig.add_subplot(2, 3, i + 1)
ax.grid(True, which="both")
outs[i].plot()
plt.show()
Despite it still can't fight with itertools.combinations for n < 15 but it is a new winner in other cases. Last but not least, numpy demonstrates its power when amount of combinations gets reaaallly big. It was able to survive while processing C(28, 14) combinations which is around 40'000'000 items of size 14
When r = k = 2, you can also use numpy.triu_indices(n, 1) which indexes upper triangle of a matrix.
idx = comb_index(5, 2)
from HYRY's answer is equivalent to
idx = np.transpose(np.triu_indices(5, 1))
but built-in, and a few times faster for N above ~20:
timeit comb_index(1000, 2)
32.3 ms ± 443 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
timeit np.transpose(np.triu_indices(1000, 1))
10.2 ms ± 25.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Not sure how it will work out performance-wise, but you can do the combinations on an index array, then extract the actual array slices with np.take:
def combs_nd(a, r, axis=0):
a = np.asarray(a)
if axis < 0:
axis += a.ndim
indices = np.arange(a.shape[axis])
dt = np.dtype([('', np.intp)]*r)
indices = np.fromiter(combinations(indices, r), dt)
indices = indices.view(np.intp).reshape(-1, r)
return np.take(a, indices, axis=axis)
>>> combs_nd([1,2,3], 2)
array([[1, 2],
[1, 3],
[2, 3]])
>>> combs_nd([[1,2,3],[4,5,6]], 2, axis=1)
array([[[1, 2],
[1, 3],
[2, 3]],
[[4, 5],
[4, 6],
[5, 6]]])

fill in numpy array without looping though all indices

I want to use a high-dimensional numpy array to store the norms of weighted sums of matrices.
For example:
mat1, mat2, mat3, mat4 = np.random.rand(3, 3), np.random.rand(3, 3), np.random.rand(3, 3), np.random.rand(3, 3)
res = np.empty((8, 7, 6, 5))
for i in range(8):
for j in range(7):
for p in range(6):
for q in range(5):
res[i, j, p, q] = np.linalg.norm(i * mat1 + j * mat2 + p * mat3 + q * mat4)
I would like to ask that are there any methods to avoid this nested loop?
Solution
Here's one way you can do it, via adding axes with None (equivalent to np.newaxis):
def weighted_norms(mat1, mat2, mat3, mat4):
P = mat1 * np.arange(8)[:, None, None]
Q = mat2 * np.arange(7)[:, None, None]
R = mat3 * np.arange(6)[:, None, None]
S = mat4 * np.arange(5)[:, None, None]
summation = S + R[:, None] + Q[:, None, None] + P[:, None, None, None]
return np.linalg.norm(summation, axis=(4, 5))
Veracity and a simple benchmark
In [6]: output = weighted_norms(mat1, mat2, mat3, mat4)
In [7]: np.allclose(output, res)
Out[7]: True
In [8]: %timeit weighted_norms(mat1, mat2, mat3, mat4)
71.3 µs ± 446 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Explanation
By adding two new axes to the np.arange objects, you can force the broadcasting you want, producing 0 * mat1, 1 * mat1, 2 * mat1 ....
The real tricky bit is then constructing the (8, 7, 6, 5, 3, 3) array (which is the shape before evaluating the norm which collapses the last two dimensions).
Notice that the summation of all the weighted 3D arrays starts with the last array, S, and progressively adds more weighted 3D arrays. The way it does this is by adding a new axis to broadcast over at each step.
For example, the shape of S is (5, 3, 3) and in order to correctly add R you need to insert a new axis. So the shape of R goes from (6, 3, 3) to (6, 1, 3, 3). This second dimension of size 1 is what allows us to broadcast the sum of S over R such that each array in the 3D S is added to each array in R (that's one level of nested loop).
Then we need to add Q (for every array in Q, for every array in R, for every array in S), so we need to insert two new axes turning Q from (7, 3, 3) to (7, 1, 1, 3, 3).
Finally, P goes from (8, 3, 3) to (8, 1, 1, 1, 3, 3).
It may help to "visualize" this by overlaying the shapes:
(5, 3, 3) <- S
:
+ (6, 1, 3, 3) <- R[:, None]
---------------------
(6, 5, 3, 3)
: :
+ (7, 1, 1, 3, 3) <- Q[:, None, None]
---------------------
(7, 6, 5, 3, 3)
: : :
+ (8, 1, 1, 1, 3, 3) <- P[:, None, None, None]
---------------------
(8, 7, 6, 5, 3, 3)
Generalizing
Here's a generalized version using a helper function for adding axes just to clean up the code a little:
from typing import Tuple
import numpy as np
def add_axes(x: np.ndarray, n: int) -> np.ndarray:
"""
Inserts `n` number of new axes into `x` from axis 1 onward.
e.g., for `x.shape == (3, 3)`, `add_axes(x, 2) -> (3, 1, 1, 3)`
"""
return np.expand_dims(x, axis=(*range(1, n + 1),))
def weighted_norms(arrs: Tuple[np.ndarray], weights: Tuple[int]) -> np.ndarray:
if len(arrs) != len(weights):
raise ValueError("Number of arrays must match number of weights")
summation = np.empty((weights[-1], *arrs[-1].shape))
for i, (x, w) in enumerate(zip(arrs[::-1], weights[::-1])):
summation = summation + add_axes(x * add_axes(np.arange(w), 2), i)
return np.linalg.norm(summation, axis=(-1, -2))
Usage:
In [10]: arrs = (mat1, mat2, mat3, mat4)
In [11]: weights = (8, 7, 6, 5)
In [12]: output = weighted_norms(arrs, weights)
In [13]: np.allclose(output, res)
Out[13]: True
In [14]: %timeit weighted_norms(arrs, weights)
109 µs ± 3.07 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Physically transposing a large non-square numpy matrix

Is there any quicker way to physically transpose a large 2D numpy matrix than array.transpose.copy()? And are there any routines for doing it with efficient memory use?
It may be worth looking at what transpose does, just so we are clear about what you mean by 'physically tranposing'.
Start with a small (4,3) array:
In [51]: arr = np.array([[1,2,3],[10,11,12],[22,23,24],[30,32,34]])
In [52]: arr
Out[52]:
array([[ 1, 2, 3],
[10, 11, 12],
[22, 23, 24],
[30, 32, 34]])
This is stored with a 1d data buffer, which we can display with ravel:
In [53]: arr.ravel()
Out[53]: array([ 1, 2, 3, 10, 11, 12, 22, 23, 24, 30, 32, 34])
and strides which tell it to step columns by 8 bytes, and rows by 24 (3*8):
In [54]: arr.strides
Out[54]: (24, 8)
We can ravel with the "F" order - that's going down the rows:
In [55]: arr.ravel(order='F')
Out[55]: array([ 1, 10, 22, 30, 2, 11, 23, 32, 3, 12, 24, 34])
While [53] is a view, [55] is a copy.
Now the transpose:
In [57]: arrt=arr.T
In [58]: arrt
Out[58]:
array([[ 1, 10, 22, 30],
[ 2, 11, 23, 32],
[ 3, 12, 24, 34]])
This a view; we can tranverse the [53] data buffer, going down rows with 8 byte steps. Doing calculations with the arrt is basically just as fast as with arr. With the strided iteration, order 'F' is just as fast as order 'C'.
In [59]: arrt.strides
Out[59]: (8, 24)
the original order:
In [60]: arrt.ravel(order='F')
Out[60]: array([ 1, 2, 3, 10, 11, 12, 22, 23, 24, 30, 32, 34])
but doing a 'C' ravel creates a copy, same as [55]
In [61]: arrt.ravel(order='C')
Out[61]: array([ 1, 10, 22, 30, 2, 11, 23, 32, 3, 12, 24, 34])
Doing a copy of the transpose makes an array that's transpose with 'C' order. This is your 'physical transpose':
In [62]: arrc = arrt.copy()
In [63]: arrc.strides
Out[63]: (32, 8)
Reshaping a transpose as done with [61] does make a copy, but usually we don't need to explicitly make the copy. I think the only reason to do so is to avoid several redundant copies in later calculations.
I assume that you need to do a row-wise operation that uses the CPU cache more efficiently if rows are contiguous in memory, and you don't have enough memory available to make a copy.
Wikipedia has an article on in-place matrix transposition. It turns out that such a transposition is nontrivial. Here is a follow-the-cycles algorithm as described there:
import numpy as np
from numba import njit
#njit # comment this line for debugging
def transpose_in_place(a):
"""In-place matrix transposition for a rectangular matrix.
https://stackoverflow.com/a/62507342/6228891
Parameter:
- a: 2D array. Unless it's a square matrix, it will be scrambled
in the process.
Return:
- transposed array, using the same in-memory data storage as the
input array.
This algorithm is typically 10x slower than a.T.copy().
Only use it if you are short on memory.
"""
if a.shape == (1, 1):
return a # special case
n, m = a.shape
# find max length L of permutation cycle by starting at a[0,1].
# k is the index in the flat buffer; i, j are the indices in
# a.
L = 0
k = 1
while True:
j = k % m
i = k // m
k = n*j + i
L += 1
if k == 1:
break
permut = np.zeros(L, dtype=np.int32)
# Now do the permutations, one cycle at a time
seen = np.full(n*m, False)
aflat = a.reshape(-1) # flat view
for k0 in range(1, n*m-1):
if seen[k0]:
continue
# construct cycle
k = k0
permut[0] = k0
q = 1 # size of permutation array
while True:
seen[k] = True
# note that this is slightly faster than the formula
# on Wikipedia, k = n*k % (n*m-1)
i = k // m
j = k - i*m
k = n*j + i
if k == k0:
break
permut[q] = k
q += 1
# apply cyclic permutation
tmp = aflat[permut[q-1]]
aflat[permut[1:q]] = aflat[permut[:q-1]]
aflat[permut[0]] = tmp
aT = aflat.reshape(m, n)
return aT
def test_transpose(n, m):
a = np.arange(n*m).reshape(n, m)
aT = a.T.copy()
assert np.all(transpose_in_place(a) == aT)
def roundtrip_inplace(a):
a = transpose_in_place(a)
a = transpose_in_place(a)
def roundtrip_copy(a):
a = a.T.copy()
a = a.T.copy()
if __name__ == '__main__':
test_transpose(1, 1)
test_transpose(3, 4)
test_transpose(5, 5)
test_transpose(1, 5)
test_transpose(5, 1)
test_transpose(19, 29)
Even though I'm using numba.njit here so that the loops in the transpose function are compiled, it's still quite a bit slower than a copy-transpose.
n, m = 1000, 10000
a_big = np.arange(n*m, dtype=np.float64).reshape(n, m)
%timeit -r2 -n10 roundtrip_copy(a_big)
54.5 ms ± 153 µs per loop (mean ± std. dev. of 2 runs, 10 loops each)
%timeit -r2 -n1 roundtrip_inplace(a_big)
614 ms ± 141 ms per loop (mean ± std. dev. of 2 runs, 1 loop each)
Whatever you do will require O(n^2) time and memory. I would assume that .transpose and .copy (written in C) will be the most efficient choice for your application.
Edit: this assumes you actually need to copy the matrix

My code is producing a logic error with recursion and I don't know how to fix it

def k_comp(n):
n_new = 0
if n == 0:
n_new = 2
if n == 1:
n_new == 1
if n > 1:
n_new = (k_comp(n-1) + k_comp(n-2))**2
return n_new
def Kseq(start, stop, step):
""" (int,int,int) -> list of integers
Kseq(0,6,1)--->
[2, 1, 9, 100, 11881, 143544361]
Kseq(2,6,2)---->
[9, 11881]
"""
final_list = []
append_this = 0
for i in range (start,stop,step):
append_this = k_comp(i)
final_list.append(append_this)
return final_list
print(Kseq(0,6,1))
Instead of the expected output it prints: [2, 0, 4, 16, 144, 16384]
The code is supposed to do this:
Input: This function is passed start (>= 0), stop (>start), and step (>= 1) values that define a sequence of numbers.
Output: This function returns a list of the corresponding K sequence.
The k sequence is k(n) = (k(n-1) + k(n-2))^2
You have mixed up assignment and equality in k_comp
You have:
if n == 1:
n_new == 1
You should have:
if n == 1:
n_new = 1
Single '=' means assign the value on the right to the variable on the left.
Double '==' means is the left value and the right value equal. In this case it will be going no it isn't equal therefore False. False is a valid python statement; it just won't be doing what you expect.
Your issue is with your second if condition in k_comp(), == is an equality test:
if n == 1:
n_new == 1
This leaves n_new = 0, so I assume you meant:
if n == 1:
n_new = 1
After making the change:
In []:
Kseq(0, 6, 1)
Out[]:
[2, 1, 9, 100, 11881, 143544361]
Note: This is going to be very inefficient because it calculates k_comp(k) multiple times, you can just construct the sequence of k, e.g.:
def k_seq():
k = [2, 1]
for _ in range(2, n):
k.append((k[-1] + k[-2])**2)
return k
def Kseq(start, stop, step):
return k_seq(stop)[start::step]
In []
Kseq(0, 6, 1)
Out[]:
[2, 1, 9, 100, 11881, 143544361]
In []:
Kseq(2, 6, 2)
Out[]:
[9, 11881]
Difference in timing:
In []:
%timeit Kseq_recursive(0, 10, 1)
Out[]:
75.8 µs ± 1.28 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In []:
%timeit Kseq_sequence(0, 10, 1)
Out[]:
4.39 µs ± 77.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Or as a generator
import itertools as it
def k_gen():
kprime, k = 2, 1
yield from (kprime, k)
while True:
kprime, k = k, (kprime + k)**2
yield k
def Kseq(start, stop, step):
return list(it.islice(k_gen(), start, stop, step))
In []:
Kseq(0, 6, 1)
Out[]:
[2, 1, 9, 100, 11881, 143544361]

Function that returns the max element over all even columns in matrices

How do you write a function that returns the maximum element among all the numbers that are in even columns of matrix1 i.e the maximum element among all elements in 0th, 2th, 4th etc. column. Precondition: matrix1 is a matrix filled with numbers with at least 1 row and 1 column.
The expected resulted is as follows:
>>> maximum_among_all_even_columns([[1,1,1,1,1,1,1],[1,10,3,20,12,6,0]])
12
Here's my attempted trial:
ncols=len(m[0])
nrows=len(m)
sums=[]
for j in range(ncols):
col_sum=0
for i in range(nrows):
if i==0 or i%2 == 0:
sums.append(m[i][j])
maxf= max(sums)
return maxf
l = [[1,1,1,1,1,1,1],[1,10,3,20,12,6,0]]
print max( sum( map(list, zip(*l)[::2]) , []) )
Note : This can be run only in Python 2.x
Explanation
I built this up as follows:
zip(*l)
creates tuples binding the elements column-wise: [(1, 1), (1, 10), (1, 3), (1, 20), (1, 12), (1, 6), (1, 0)]
zip(*l)[::2]
takes even steps in the list: [(1, 1), (1, 3), (1, 12), (1, 0)]
map( list,zip(*l)[::2] )
converts the above tuples into lists (since we can modify lists): [[1, 1], [1, 3], [1, 12], [1, 0]]
sum( map(list, zip(*l)[::2]) , [])
joins all the inner lists to single list: [1, 1, 1, 3, 1, 12, 1, 0]
And finally
max( sum( map(list, zip(*l)[::2]) , []) )
highest value of that list: 12
If you want to return the max for the even cols:
import random
n = 10
m = 10
matrix = [[random.randint(0,100) for i in range(n)] for j in range(m)]
Sol. 1:
%%timeit
max([element for row in matrix for col, element in enumerate(row) if col%2 == 0])
10000 loops, best of 3: 19.3 µs per loop
Sol. 2:
%%timeit
res = max([row[i] for row in matrix for i in range(0,len(row),2)])
100000 loops, best of 3: 7.91 µs per loop
Sol. 3:
%%timeit
res = max([element for row in matrix for element in row[::2]])
100000 loops, best of 3: 4.92 µs per loop
Sol. 4:
%%timeit
res = matrix[0][0]
for j in range(len(matrix[0])//2):
for i in range(len(matrix)):
if matrix[i][2*j] > res:
res = matrix[i][2*j]
100000 loops, best of 3: 8.28 µs per loop
Sol. 5:
%%timeit
max([max(e) for e in [col for col in zip(*matrix)][::2]])
100000 loops, best of 3: 4.48 µs per loop
The solution 5 (using zip()) is slightly better. Tested on Dual Intel(R) Xeon(R) CPU E5-2630 v3 # 2.40GHz (32 cores) and Python 3.5.2 :: Anaconda custom (64-bit)
UPDATE
Add timings...
UPDATE 2
Solution slicing.
UPDATE 3
I add some code to generate the matrix, 'translate' the zip() solution to Python3 and test the solutions with matrices 10*10. I put all the solutions together.
This is pretty straightforward, I don't think it requires much explanation.
The difficulty might be that we are used to iterating over lines then over columns, but here it is the other way round.
maxValue = matrix[0][0]
for j in range(len(matrix[0])//2) :
for i in range(len(matrix)) :
if matrix[i][2*j] > maxValue :
maxValue = matrix[i][2*j]
EDIT: Changed max to maxValue, so that the max built-in function is not overwritten.

Categories