Updating list elements by indices [duplicate] - python

This question already has answers here:
Unable to use *= python operator in list comprehension [duplicate]
(2 answers)
Closed 4 years ago.
I want to add 5 to my specific list indices using list comprehension
Input
arr=[0,0,0,0,0]
Output
arr=[0,0,5,5,5]
I tried
[arr[i]+=5 for i in range(2,4)]
but it gives an error.

Don't use list comprehensions for side effects. The purpose of a list comp is to create a new list. To that end, I believe you can use enumerate + range here -
l, u = 2, 4
arr = [x + 5 if i in range(l, u + 1) else x for i, x in enumerate(arr)]
print(arr)
[0, 0, 5, 5, 5]
In python3, this should be very efficient because in checks on range objects are O(1) time. On python2, it would be faster to perform a boolean check (this is what an in check on range does in python3) -
arr = [x + 5 if l <= i <= u else x for i, x in enumerate(arr)]
However, keep in mind that a for loop would be the most efficient method to use here.
for i in range(l, u + 1):
arr[i] += 5
print(arr)
[0, 0, 5, 5, 5]
Because,
You only iterate over the indices you need to. Nothing more, nothing less
You make changes in place, rather than creating a new list

You can also use addition of lists by slicing them here :
arr[0:2] + [i+5 for i in arr[2:5]]
[0, 0, 5, 5, 5]

You can also try without for loop something like this:
list_1=[0,0,0,0,0]
b=list(range(2,5))
list(map(lambda x,y:list_1.__setitem__(x,list_1[x]+5),b,list_1))
print(list_1)
output:
[0, 0, 5, 5, 5]

Here's a Pythonic way to use a list comprehension to replace some indices. In this case, every index except the 2 first ones:
>>> arr = [0,0,0,0,0]
>>> arr[2:] = [i + 5 for i in arr[2:]]
>>> arr
[0, 0, 5, 5, 5]
Note that arr isn't an array, but a list. With numpy, the operation becomes easier:
>>> import numpy as np
>>> arr = np.array([0, 0, 0, 0, 0])
>>> arr
array([0, 0, 0, 0, 0])
>>> arr[2:] += 5
>>> arr
array([0, 0, 5, 5, 5])
It also works if you have a list of indices:
>>> arr = np.array([0, 0, 0, 0, 0])
>>> arr
array([0, 0, 0, 0, 0])
>>> arr[[2, 3, 4]]
array([0, 0, 0])
>>> arr[[2, 3, 4]] += 5
>>> arr
array([0, 0, 5, 5, 5])

Related

How do I rewrite the following code in Python without for loops?

I have an array "b" of size L^2 x L*(L+1), and an array "a" of size L x L.
currently my code is
for i in range (L):
for j in range (L):
b[i+j*L,i+j*(L+1)] = a[j,i]
What this means is that, for example for L=2, if the 2x2 array "a" has the form
ab
cd
then I want the 4x6 array "b" to be
a00000
0b0000
000c00
0000d0
How do I rewrite the same thing without using for loops?
What you want is to fill the diagonal of matrix B with the values of (flattened) A. Numpy has functions for this:
https://numpy.org/doc/stable/reference/generated/numpy.ndarray.flatten.html
https://numpy.org/doc/stable/reference/generated/numpy.fill_diagonal.html
import numpy as np
# Set sample data
a = np.array([[1, 2], [3, 4]])
b = np.zeros([4,6])
# This is it:
np.fill_diagonal(b, a.flatten())
If you don't want to use a library, for example because this is a programming assignment, you can represent matrices as nested lists and use a list comprehension, as this:
# Prepare data
a = [[1, 2], [3, 4]]
L = len(a)
# "Build" the result
b = [[a[i//L][i%L] if i == j else 0 for j in range(L*(L+1))] for i in range(L*L)]
# Same, with better formatting:
b = [[a[i//L][i%L]
if i == j else 0
for j in range(L*(L+1))]
for i in range(L*L)]
# b will be = [[1, 0, 0, 0, 0, 0],
# [0, 2, 0, 0, 0, 0],
# [0, 0, 3, 0, 0, 0],
# [0, 0, 0, 4, 0, 0]]
Anyway you need to iterate through the items in 'a', so you are just replacing the 'for' constructions by a list comprehension. This might be more efficient for large matrices but arguably less clear.
Generalizing the answer from Milo:
L = (a.shape)[0]
b = np.zeros([L*L, L*(L+1)])
np.fill_diagonal(b, a.flatten())

Add a number to list in a specified index range

Input
l = [0, 0, 1, 2, 3]
I want to add 1 to index range from 2 to 3
so output should be
l = [0, 0, 2, 3, 3]
l[2:3] = l[2:3] + 1
The easiest way would be to use numpy, it's quite optimized and uses C/C++ loops under the hood, so it's blazingly fast:
>>> import numpy as np
>>> a = [0, 0, 1, 2, 3]
>>> b = np.array(a)
>>> b[2:4] += 1
>>> b
array([0, 0, 2, 3, 3])
>>>
You can try this:
for i in range(2, 4):
l[i] += 1
A possible solution can make use of list-comprehension:
l[2:4] = [x+1 for x in l[2:4]]
For a hilariously overblown solution:
from operator import add
l = [0, 0, 1, 2, 3]
deltas = [0, 0, 1, 1, 1]
result = list(map(add, l, deltas))
note that this does not modify l, but creates a new list in result

Repeating each element of a vector by a number of times provided by another counts vector [duplicate]

Say I have an array with longitudes, lonPorts
lonPort =np.loadtxt('LongPorts.txt',delimiter=',')
for example:
lonPort=[0,1,2,3,...]
And I want to repeat each element a different amount of times. How do I do this? This is what I tried:
Repeat =[5, 3, 2, 3,...]
lonPort1=[]
for i in range (0,len(lenDates)):
lonPort1[sum(Repeat[0:i])]=np.tile(lonPort[i],Repeat[i])
So the result would be:
lonPort1=[0,0,0,0,0,1,1,1,2,2,3,3,3,...]
The error I get is:
list assignment index out of range
How do I get rid of the error and make my array?
Thank you!
You can use np.repeat():
np.repeat(a, [5,3,2,3])
Example:
In [3]: a = np.array([0,1,2,3])
In [4]: np.repeat(a, [5,3,2,3])
Out[4]: array([0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 3])
Without relying on numpy, you can create a generator that will consume your items one by one, and repeat them the desired amount of time.
x = [0, 1, 2, 3]
repeat = [4, 3, 2, 1]
def repeat_items(x, repeat):
for item, r in zip(x, repeat):
while r > 0:
yield item
r -= 1
for value in repeat_items(x, repeat):
print(value, end=' ')
displays 0 0 0 0 1 1 1 2 2 3.
Providing a numpy-free solution for future readers that might want to use lists.
>>> lst = [0,1,2,3]
>>> repeat = [5, 3, 2, 3]
>>> [x for sub in ([x]*y for x,y in zip(lst, repeat)) for x in sub]
[0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 3]
If lst contains mutable objects, be aware of the pitfalls of sequence multiplication for sequences holding mutable elements.

group elements in an array and sum

I have the following array
a = [0,0,1,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,0,0,1]
I want to group every 3rd element and sum all of the elements within each group. So I can get a new array with a new size showing this sum
b = [1,0,2,0,3,0,1]
Any suggestions?
Simply, most pythonicly would be the following
b = [sum(a[i:i+3]) for i in range(0, len(a), 3)]
where your input array is a.
>>> a = [0,0,1,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,0,0,1]
>>> b = [sum(a[i:i+3]) for i in range(0, len(a), 3)]
>>> b
[1, 0, 2, 0, 3, 0, 1]
You can split in chunk and sum:
step = 3
[sum(a[i:i+step]) for i in range(0, len(a),step)]
[1, 0, 2, 0, 3, 0, 1]
If the length is not the multiple of step, last chunk might be smaller.
Maybe something like this:
a = [0,0,1,0,0,0,1,1,0,0,0,0,1,1,1,0,0,0,0,0,1]
b = []
for i in range(0,len(a),3):
b.append(sum(a[i:i+3]))
print b
Output:
[1, 0, 2, 0, 3, 0, 1]
Another option using groupby from itertools:
from itertools import groupby
[sum(v for _, v in g) for _, g in groupby(enumerate(a), key = lambda x: x[0]/3)]
# [1, 0, 2, 0, 3, 0, 1]
Or another way to use zip:
[sum(v) for v in zip(a[::3], a[1::3], a[2::3])]
# [1, 0, 2, 0, 3, 0, 1]

Add element into list at even indexes [duplicate]

This question already has answers here:
python: most elegant way to intersperse a list with an element
(15 answers)
Closed 6 years ago.
I have a list like
[1, 2, 3, 4, 5]
and I want to add zeroes at odd indexes:
[1, 0, 2, 0, 3, 0, 4, 0, 5]
My first thought was to create a list with zeroes and replace them with the values from the original list.
listOfZeros = [0] * (2*len(list)-1)
j = 0
for i in range(0, len(listOfZeros)):
if (i%2 == 0):
listOfZeros[i] = h_temp[j]
j += 1
This actually works, but I do dislike for loops and adding another counter j. Isn't there a better way by using slicing?
You can use insert(). Looking at your output, assuming you are not counting index 0 as even.
a = [1,2,3,4,5]
for x in range(len(a)):
a.insert(2*x+1, 0)
one way is by using zip:
a = [1, 2, 3, 4, 5]
d = [x for t in zip (a, [0] * len(a)) for x in t][:-1]
When you use zip, you create list of tuples.
a = [1,2,3,4,5]
b = [0,0,0,0,0]
c = zip(a,b)
#zip (a,b) creates [(1,0),(2,0),(3,0),(4,0),(5,0)]
Then you loop over the set of tuples to arrange them into list:
d = [x for t in c for x in t] #creates [1,0,2,0,3,0,4,0,5,0]
and cut the last element (since you end with 5)
[x for t in c for x in t][:-1] #take out the last 0
#resulting in [1,0,2,0,3,0,4,0,5]
then you are done.
You can do it with a generator:
def zero_on_odd(mylist):
for i in mylist:
yield i
yield 0
a = [1, 2, 3]
with_zeros = list(zero_on_odd(a))[:-1]
If you want to go functional...
from itertools import chain, repeat
_list = [1,2,3,4,5]
list(chain(*zip(_list, repeat(0))))[:-1]
# [1, 0, 2, 0, 3, 0, 4, 0, 5]
If you want to be silly...
[int(i) for i in '0'.join(str(i) for i in _list)]
# still [1, 0, 2, 0, 3, 0, 4, 0, 5]
Or, if you want to be functional AND silly...
map(int, '0'.join(map(str, _list)))
# really, it's still [1, 0, 2, 0, 3, 0, 4, 0, 5]
# except in Python 3.X, there it's a map object...
But, you should probably opt for one of the custom generator solutions.
For the fun of it, here is an itertools solution:
from itertools import islice, chain
data = [1,2,3,4,5]
print list(islice(chain.from_iterable((x, 0) for x in data), 0, 2 * len(data)-1))
Giving:
[1, 0, 2, 0, 3, 0, 4, 0, 5]
Another zip way:
>>> li
[1, 2, 3, 4, 5]
>>> [e for t in zip(li,[0]*(len(li)-1)) for e in t]+[li[-1]]
[1, 0, 2, 0, 3, 0, 4, 0, 5]
You can also use range and slice assignment:
>>> li=[1,2,3,4,5]
>>> for i in range(1,len(li)+len(li)-1, 2): li[i:i]=[0]
...
>>> li
[1, 0, 2, 0, 3, 0, 4, 0, 5]
And, a list comprehension:
>>> [li[i/2] if not i%2 else 0 for i in range(len(li)*2-1)]
[1, 0, 2, 0, 3, 0, 4, 0, 5]
A hacky way:
>>> ls1 = [1, 2, 3, 4, 5]
>>> ls2 = []
>>> list(ls2.extend([n, 0]) for n in ls1)
[None, None, None, None, None]
>>> ls2
[1, 0, 2, 0, 3, 0, 4, 0, 5, 0]

Categories