using for loop with in a for loop - python-3.x

For a given array v=[1,2,3] I am trying to print the sum of the product of each element with s for a range of s
import numpy as np
v=[1,2,3]
for s in range(0,5):
for i in range (0,3):
tot= np.multiply(v[i],s)
b.append(tot)
print (b)
my output is
[0, 0, 0, 1, 2, 3, 2, 4, 6, 3, 6, 9, 4, 8, 12]
I am trying to get the out put as
[[0, 0, 0], [1, 2, 3], [2, 4, 6], [3, 6, 9], [4, 8, 12]]
I am not quite sure how the second for loop is working inside the first for loop. If someone can explain that, it would be wonderful.

You'd create a new list for each iteration of the outer for loop:
v=[1,2,3]
b = []
for s in range(0,5):
result = []
for i in range (0,3):
tot= np.multiply(v[i],s)
result.append(tot)
b.append(result)
print (b)
You could just use * to multiply values, and you can iterate directly over v (no need to use a range)`:
v = [1, 2, 3]
b = []
for s in range(5):
result = []
for i in v:
result.append(i * s)
b.append(result)
You can replace both loops with list comprehensions:
b = [[i * s for i in v] for s in range(5)]

import numpy as np
v=np.array([1,2,3])
b=[]
for s in range(0,5):
b.append(list(v*s))
print (b)
Should do what you want. Don't forget numpy's extremely powerful broadcasting capability.

v=[1,2,3]
b=[]
for s in range(0,5):
b.append([])
for i in range (0,3):
tot= np.multiply(v[i],s)
b[s].append(tot)
print(b)

Related

how to convert the following code into list comprehensions

I have written the code using for loop, i want the code into list comprehension.
mainLst = [[2],[3],[4],[5],[6],[7],[8]]
lst2 = [[],[],[],[]]
const = 0
for i in range(4):
k = const
for j in range(4):
lst2[const].append(mainLst[k][0])
k += 1
const += 1
print(lst2)
Expecting the above code into list comprehension.
If you want to transform just a loop part and still use mainLst variable, then it will look like:
mainLst = [[2],[3],[4],[5],[6],[7],[8]]
lst2 = [[mainLst[j + k][0] for k in range(4)] for j in range(4)]
print(lst2)
# or if you still want to have initialized lst2 array, then:
lst2 = [[],[],[],[]]
[lst2[j].extend([mainLst[j + k][0] for k in range(4)]) for j in range(4)]
print(lst2)
Both output the same:
[[2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7], [5, 6, 7, 8]]
Can be done without creating other variables like k and const
lst2 = [[i for i in range(j, j + 4)] for j in range(2, 6)]
You can use the operator itemgetter with slice to get slices of sublists from the list and chain.from_iterable to merge sublists into one list:
from operator import itemgetter
from itertools import chain
mainLst = [[2],[3],[4],[5],[6],[7],[8]]
[list(chain.from_iterable(itemgetter(slice(i, i + 4))(mainLst))) for i in range(4)]
# [[2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7], [5, 6, 7, 8]]

How to use list comprehensions for this?

I want to take input of 2 numbers: the number of rows and the number of columns. I then want to use these to output a matrix numbered sequentially. I want to do this using a list comprehension. The following is a possible output.
>>>> my_matrix = matrix_fill(3, 4)
>>>> my_matrix
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
I am using the following code to output a sequentially numbered list:
def matrix_fill(num_rows, num_col):
list=[i for i in range(num_col)]
return (list)
I cannot, however, figure out how to make the sequential list of numbers break into the separate lists as shown in the output based on num_rows.
I don't think you need itertools for that. The range function can take a step as a parameter. Like this:
def matrix_fill(rows,cols):
return [[x for x in range(1,rows*cols+1)][i:i+cols] for i in range(0,rows*cols,cols)]
And then it works as expected.
>>> matrix_fill(3,4)
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
Let's break this down a little bit and understand what's happening.
>>> [x for x in range(1,3*4+1)]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
So what we want to do is to get a new slice every four elements.
>>> [x for x in range(1,3*4+1)][0:4]
[1, 2, 3, 4]
>>> [x for x in range(1,3*4+1)][4:8]
[5, 6, 7, 8]
>>> [x for x in range(1,3*4+1)][8:12]
[9, 10, 11, 12]
So we want to iterate over the elements of the list[x for x in range(1,3*4+1)] of length "rows*cols" ( 3 * 4 ), create a new slice every "cols" number of elements, and group these slices under a single list. Therefore, [[x for x in range(1,rows*cols+1)][i:i+cols] for i in range(0,rows*cols,cols)] is a suitable expression.
Nest a list comprehension inside another one, use itertools.count() to generate the sequence:
import itertools
rows = 3
cols = 4
count_gen = itertools.count() # pass start=1 if you need the sequence to start at 1
my_matrix = [[next(count_gen) for c in range(cols)] for r in range(rows)]
print(my_matrix)
# prints: [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
# As a function
def matrix_fill(rows, cols):
count_gen = itertools.count()
return [[next(count_gen) for c in range(cols)] for r in range(rows)]
If you used the numpy module, the method is extremely simple, with no list comprehension needed.
my_matrix = np.arange(1, 13).reshape(3,4)
Printing the variable my_matrix shows
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]

How to write a function where the original list of integers is changed without using return?

Let us say we have a list of integers:
list = [6, 4, 1, 4, 4, 4, 4, 4, 2, 1]
I now wrote a function which returns another list with all the integers from the list above without repeats.
def no_repeats(s):
new_list = []
for number in s:
if new_list.count(number) < 1:
new_list.append(number)
return(new_list)
The new_list returns [6, 4, 1, 2] which is good! My question is how I would now write two similar functions:
A function clean(s) which does not return a new list like the function above, but changes the original list by deleting all the numbers that repeat. Thus, the result has to be the same and the function must not include "return" or create a new list. It must only clean the original list.
A function double(s) which, again, changes the original list (does not return a new list!) but this time, by doubling every number in the original list. Thus, double(list) should change the original list above to:
[6, 6, 4, 4, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 1, 1]
Thank you for all the help!
Removing duplicates inplace without preserving the order:
def no_repeats(L):
L[:] = set(L)
There are several variations possible (preserve order, support non-hashable items, support item that do not define total ordering) e.g., to preserve order:
from collections import OrderedDict
def no_repeats(L):
L[:] = OrderedDict.fromkeys(L)
To double each element's value inplace:
def double(L):
for i in range(len(L)):
L[i] *= 2
To duplicate each element:
def duplicate_elements(L):
L[:] = [x for x in L for _ in range(2)]
>>> def clean(s):
... s[:] = [s[i] for i in range(len(s)) if s[i] not in s[:i]]
...
>>> st = [1, 2, 3, 2, 1]
>>> clean(st)
>>> st
[1, 2, 3]
>>> def double(s):
... s[:] = [s[i//3] for i in range(3*len(s)) if i % 3]
...
>>> st = [1, 2, 3, 2, 1]
>>> double(st)
>>> st
[1, 1, 2, 2, 3, 3, 2, 2, 1, 1]
neither is particularly efficient nor pythonic, yet do address the OP question
def double(s):
... s[:] = [s[i//2] for i in range(2*len(s))]
will also do the trick, with a little less obsfucation

Returning the N largest values' indices in a multidimensional array (can find solutions for one dimension but not multi-dimension)

I have a numpy array X, and I'd like to return another array Y whose entries are the indices of the n largest values of X i.e. suppose I have:
a =np.array[[1, 3, 5], [4, 5 ,6], [9, 1, 7]]
then say, if I want the first 5 "maxs"'s indices-here 9, 7 , 6 , 5, 5 are the maxs, and their indices are:
b=np.array[[2, 0], [2 2], [ 2 1], [1 1], [0 , 2])
I've been able to find some solutions and make this work for a one dimensional array like
c=np.array[1, 2, 3, 4, 5, 6]:
def f(a,N):
return np.argsort(a)[::-1][:N]
But have not been able to generate something that works in more than one dimension. Thanks!
Approach #1
Get the argsort indices on its flattened version and select the last N indices. Then, get the corresponding row and column indices -
N = 5
idx = np.argsort(a.ravel())[-N:][::-1] #single slicing: `[:N-2:-1]`
topN_val = a.ravel()[idx]
row_col = np.c_[np.unravel_index(idx, a.shape)]
Sample run -
# Input array
In [39]: a = np.array([[1,3,5],[4,5,6],[9,1,7]])
In [40]: N = 5
...: idx = np.argsort(a.ravel())[-N:][::-1]
...: topN_val = a.ravel()[idx]
...: row_col = np.c_[np.unravel_index(idx, a.shape)]
...:
In [41]: topN_val
Out[41]: array([9, 7, 6, 5, 5])
In [42]: row_col
Out[42]:
array([[2, 0],
[2, 2],
[1, 2],
[1, 1],
[0, 2]])
Approach #2
For performance, we can use np.argpartition to get top N indices without keeping sorted order, like so -
idx0 = np.argpartition(a.ravel(), -N)[-N:]
To get the sorted order, we need one more round of argsort -
idx = idx0[a.ravel()[idx0].argsort()][::-1]

something unexpected in python when i design a nested list

i want to turn 'a' to a nested list:
so i design a while loop:
a = [1,2,3,4,5,6,7,8,9,10,11,12]
ll = l = []
m,f,k = 1,0,4
while m <= 3:
l.append(a[int(f):int(k)])
f = f + 4
k = k + 4
ll.append(l)
m = m+1
print(ll)
i want [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]] rather than[[1, 2, 3, 4], [...], [5, 6, 7, 8], [...], [9, 10, 11, 12], [...]]
why the result contains [...]
where is the problem in while loop
Lists are mutable objects, so ll=l=[] means that ll is the
exact same object as l. This can be verified using the is operator:
>>> ll is l
True
This can be demonstrated as follows:
>>> a=b=[]
>>> a
[]
>>> b
[]
>>> a.append(1)
>>> a
[1]
>>> b # For all practical purposes, a is identical to b
[1]
>>> a is b
True
Therefore, the line ll.append(l) creates a recursive object!
Using the pprint module after running the above code states this clearly:
>>> # Run the posted code
>>> import pprint
>>> pprint.pprint(ll)
[[1, 2, 3, 4],
<Recursion on list with id=2032187430600>,
[5, 6, 7, 8],
<Recursion on list with id=2032187430600>,
[9, 10, 11, 12],
<Recursion on list with id=2032187430600>]
The ll list isn't actually necessary, since the l.append method already
appends the newly generated list to the l object and creates a 2D list:
>>> q=[[1,2,3,4]]
>>> q.append([5,6,7,8])
>>> q
[[1, 2, 3, 4], [5, 6, 7, 8]]
The code can be rewritten as follows:
a = [1,2,3,4,5,6,7,8,9,10,11,12]
l = list()
f,k = 0,4
# range(1,4) iterates m through the values [1, 2, 3]
# This includes the first but excludes the last
for m in range(1,4):
# f and k are already integers, so no need for typecasting
# This append statement will append the 1D slice as a single unit
l.append(a[f:k])
# a += 1 is the same as a = a + 1 but is more compact
f += 4
k += 4
print(l)
# Will be [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
If you really need to create two separate empty lists, it is better to do it this way:
>>> q=list()
>>> w=list()
>>> q is w
False

Resources