Replace every second and third item in list with 0 - python

I have a randomly generated list of items, and I want to replace every second and third item in that list with the number 0. For the replacement of every second item I have used the code below.
import random
x = [random.randint(0,11) for x in range(1000)]
y = [random.randint(0,11) for x in range(1000)]
a = (x + y)
a[::2] = [0]*(2000//2)
print(a)
It works fine, but I can't use the same method with replacing every third item since it gives me an error
attempt to assign sequence of size 666 to extended slice of size 667
I thought of using list comprehension, but I'm unsure of how to execute it, and I could not find a definite answer in my research.

In the case of every second element, the size of [0]*(2000//2) is equal to the size of a[::2], which is 1000. That is why you are not getting an error. But in the case of a[::3], there are 667 elements and [0]*(2000//3) returns a list of size 666, which is not possible to assign. You can use math.ceil to solve this issue. As:
import random
from math import ceil
x = [random.randint(0, 11) for x in range(1000)]
y = [random.randint(0, 11) for x in range(1000)]
a = (x + y)
index = 2
a[::index] = [0] * ceil(2000/index)
print(a)

You can simply replace 2000//2 with len(a[::2]) like this
import random
x = [random.randint(0,11) for x in range(1000)]
y = [random.randint(0,11) for x in range(1000)]
a = (x + y)
a[::2] = [0]*len(a[::2])
print(a)
b = (x + y)
b[::3] = [0]*len(b[::3])
print(b)

Something like this, where every other or every 3rd become zero.
[0 if (i+1)%2==0 or (i+1)%3==0 else x for i, x in enumerate(a)]

Not quite as neat as subscripting the list outright, but this can be done with the % operator and indices:
import random
x = [random.randint(0,11) for x in range(1000)]
y = [random.randint(0,11) for x in range(1000)]
a = (x + y)
for i, v in enumerate(a):
if (i+1) % 2 == 0 or (i+1) % 3 == 0:
a[i] = 0
# if you prefer a comprehension:
a = [0 if (i+1) % 3 == 0 or (i+1) % 2 == 0 else v for i, v in enumerate(a)]
print(a)
[3, 6, 0, 5, 1, 0, 8, 5, 0, 1, 5, 0, 2, 3, 0, 7, 9, 0, ... ]

Others have explained how replacement is done, but the same result could be achieved by not generating these random throwaway numbers in the first place.
from random import randint
N = 2_000
[randint(0, 11) if (x % 2) * (x % 3) != 0 else 0
for x in range(1, N + 1)]

To get this done efficiently I would recommend using the numpy module here is the code
import numpy as np
a = np.random.randint(0,12,(2000,))
a[::2] = 0
a[::3] = 0
a = a.tolist()
print(a)
np.random.randint takes in 3 arguments. the first is the lower bound inclusive, the second is the upper bound exclusive and the third is a tuple of the array dimensions. In this case a 1-d array of 2000. using numpy you can just use slicing to set the parts you want to zero and then use tolist() to convert back to a list if you need to or keep it as a numpy array.

Related

Sum of random list numbers after 1st negative number

import random
def mainlist(list, size, min, max):
for i in range(size):
list.append(random.randint(min, max))
print(list)
def counterlist(list):
for i in list:
if i<0:
x=sum(list[(list.index(i)+1):])
print('Reqemlerin cemi:', x)
break
list = []
mainlist(list, 10, -10, 30)
counterlist(list)
I need to calculate sum of numbers after 1st negative number in this random list, did it in second function but want to know is there way not using the sum() function?
Explicitly using an iterator makes it nicer and more efficient:
def counterlist(lst):
it = iter(lst)
for i in it:
if i < 0:
print('Reqemlerin cemi:', sum(it))
No idea why you wouldn't want to use the sum function, that's absolutely the right and best way to do it.
Try this:
import random
lst = [random.randint(-10, 30) for _ in range(10)]
print(sum(lst[next(i for i, n in enumerate(lst) if n < 0) + 1:]))
First you generate the list lst. Then, you iterate over your list and you find the first negative element with next(i for i, n in enumerate(lst) if n < 0). Finally, you compute the sum of the portion of the list you're interested about.
If you really don't want to use sum but keep things concise (and you're using python >= 3.8):
import random
lst = [random.randint(-10, 30) for _ in range(10)]
s = 0
print([s := s + x for x in lst[next(i for i, n in enumerate(lst) if n < 0) + 1:]][-1])
Assuming there's a negative value in the list, and with a test list "a":
a = [1,2,3,-7,2,3,4,-1,23,3]
sum(a[(a.index([i for i in a if i < 0][0]) + 1):])
Evaluates to 34 as expected. Could also add a try/except IndexError with a simple sum to catch if there's no negative value.
Edit: updated the index for the search.
Yes, you can iterate over the elements of the list and keep adding them to some var which would store your result. But what for? sum approach is much more clear and python-ish.
Also, don't use list as a list name, it's a reserved word.
# After you find a first negative number (at i position)
j = i + 1
elements_sum = 0
while j < len(list):
elements_sum += list[j]
j += 1
Not as good as the marked answer, but just to know how to make use of numpy, being sure there is a negative number in the list.
Sample list: lst = [12, 2, -3, 4, 5, 10, 100]
You can get your result using np.cumsum:
import numpy as np
np_lst = np.array(lst)
cum_sum = np.cumsum(np_lst)
res = cum_sum[-1] - cum_sum[np_lst<0][0]
res #=> 119
First of all don't use list as a variable name, it's a reserved keyword. Secondly, make your loop as follows:
for index, x in enumerate(list_):
if x < 0:
sum_ = sum(list_[(index + 1):])
print('Reqemlerin cemi:', sum_)
break
That way, you don't need to find a value.
At last if you don't want to use sum
found_negative = false
sum_ = 0
for x in list_:
if found_negative:
sum_ += x
elif x < 0:
found_negative = true
print('Reqemlerin cemi:', sum_)

Find first non zero numbers in an array (zeroes can be anywhere)

Suppose we have an array: x = [10,0,30,40]. I would like to extract the first non zero element and store it in a different variable, say y. In this example, y = 10. We can also have many zeros, x = [0,0,30,40], which should give y = 30 as the extracted value.
I tried a Python snippet like this:
i = 0
while x[i] != 0:
y = arr[i]
if x[i] == 0:
break
This only works if the array is [10,0,30,40]. It does not work if I have 0,20,30,40. The loop would stop before that. What is an efficient way to implement this? I try not to use any special Numpy functions, just generic common loops because I might need to port it to other languages.
You can do this
x = [10,0,30,40]
for var in x:
if var != 0:
y = var
break
You could use list comprehension to get all the non-zero values, then provided there are some non-zero values extract the first one.
x = [10,0,30,40]
lst = [v for v in x if v != 0]
if lst:
y = lst[0]
print(y)
The problem with your code is that you don't increment i so it's stuck on the first element. So what you could do to keep the code portable is:
while x[i] != 0:
y = x[i]
if x[i] == 0:
break
i+=1
This code is still not clean, because if there is no 0 in your array then you will get an IndexError as soon as you reach the end.
I'm kind of new to this but i think this should work:
x = [0, 12, 24, 32, 0, 11]
y = []
for num in x:
if num != 0:
y.append(num)
break
print(y)
You can use the next function:
x = [10,0,30,40]
next(n for n in x if n) # 10
x = [0,0,30,40]
next(n for n in x if n) # 30
if you need to support the absence of zero in the list, you can use the second parameter of the next() function:
x = [0,0,0,0]
next((n for n in x if n),0) # 0

Back and forth loop Python

I want to create an infinite loop that counts up and down from 0 to 100 to 0 (and so on) and only stops when some convergence criterion inside the loop is met, so basically something like this:
for i in range(0, infinity):
for j in range(0, 100, 1):
print(j) # (in my case 100 lines of code)
for j in range(100, 0, -1):
print(j) # (same 100 lines of code as above)
Is there any way to merge the two for loops over j into one so that I don't have write out the same code inside the loops twice?
Use the chain method of itertools
import itertools
for i in range(0, infinity):
for j in itertools.chain(range(0, 100, 1), range(100, 0, -1)):
print(j) # (in my case 100 lines of code)
As suggested by #Chepner, you can use itertools.cycle() for the infinite loop:
from itertools import cycle, chain
for i in cycle(chain(range(0, 100, 1), range(100, 0, -1))):
....
As well as the other answers you can use a bit of maths:
while(True):
for i in range(200):
if i > 100:
i = 200 - i
Here's yet another possibility:
while notConverged:
for i in xrange(-100, 101):
print 100 - abs(i)
If you've got a repeated set of code, use a function to save space and effort:
def function(x, y, x, num_from_for_loop):
# 100 lines of code
while not condition:
for i in range(1, 101):
if condition:
break
function(x, y, z, i)
for i in range(100, 0, -1):
if condition:
break
function(x, y, z, i)
You could even use a while True
If you're using Python 3.5+, you can using generic unpacking:
for j in (*range(0, 100, 1), *range(100, 0, -1)):
or prior to Python 3.5, you can use itertools.chain:
from itertools import chain
...
for j in chain(range(0, 100, 1), range(100, 0, -1)):
up = True # since we want to go from 0 to 100 first
while True: #for infinite loop
# For up == True we will print 0-->100 (0,100,1)
# For up == False we will print 100-->0 (100,0,-1)
start,stop,step = (0,100,1) if up else (100,0,-1)
for i in range(start,stop,step):
print(i)
up = not up # if we have just printed from 0-->100 (ie up==True), we want to print 100-->0 next so make up False ie up = not up( True)
# up will help toggle, between 0-->100 and 100-->0
def up_down(lowest_value, highest_value):
current = lowest_value
delta = 1
while True: # Begin infinite loop
yield current
current += delta
if current <= lowest_value or current >= highest_value:
delta *= -1 # Turn around when either limit is hit
This defines a generator, which will continue to yield values for as long as you need. For example:
>>> u = up_down(0, 10)
>>> count = 0
>>> for j in u:
print(j) # for demonstration purposes
count += 1 # your other 100 lines of code here
if count >= 25: # your ending condition here
break
0
1
2
3
4
5
6
7
8
9
10
9
8
7
6
5
4
3
2
1
0
1
2
3
4
I became curious if it's possible to implement such kind of triangle oscillator without conditions and enumerations. Well, one option is the following:
def oscillator(magnitude):
i = 0
x = y = -1
double_magnitude = magnitude + magnitude
while True:
yield i
x = (x + 1) * (1 - (x // (double_magnitude - 1))) # instead of (x + 1) % double_magnitude
y = (y + 1) * (1 - (y // (magnitude - 1))) # instead of (y + 1) % magnitude
difference = x - y # difference ∈ {0, magnitude}
derivative = (-1 * (difference > 0) + 1 * (difference == 0))
i += derivative
The idea behind this is to take 2 sawtooth waves with different periods and subtract one from another. The result will be a square wave with values in {0, magnitude}. Then we just substitute {0, magnitude} with {-1, +1} respectively to get derivative values for our target signal.
Let's look at example with magnitude = 5:
o = oscillator(5)
[next(o) for _ in range(21)]
This outputs [0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0].
If abs() is allowed, it can be used for simplicity. For example, the following code gives the same output as above:
[abs(5 - ((x + 5) % 10)) for x in range(21)]
This is more of a partial answer than a direct answer to your question, but you can also use the notion of trigonometric functions and their oscillation to imitate a 'back and forth' loop.
If we have a cos function with an amplitude of 100, shifted left and upwards so that f(x) = 0 and 0 <= f(x) <= 100, we then have the formula f(x) = 50(cos(x-pi)+1) (plot of graph may be found here. The range is what you require, and oscillation occurs so there's no need to negate any values.
>>> from math import cos, pi
>>> f = lambda x: 50*(cos(x-pi)+1)
>>> f(0)
0.0
>>> f(pi/2)
50.0
>>> f(pi)
100.0
>>> f(3*pi/2)
50.0
>>> f(2*pi)
0.0
The issue of course comes in that the function doesn't give integer values so easily, thus it's not that helpful - but this may be useful for future readers where trigonometric functions might be helpful for their case.
I had a similar problem a while ago where I also wanted to create values in the form of an infinite triangle wave, but wanted to step over some values. I ended up using a generator (and the range function as other also have been using):
def tri_wave(min, max, step=1):
while True:
yield from range(min, max, step)
yield from range(max, min, -1 * step)
With carefully selected values on min, max and step (i.e. evenly divisible),
for value in tri_wave(0, 8, 2):
print(value, end=", ")
I get the min and max value only once, which was my goal:
...0, 2, 4, 6, 8, 6, 4, 2, 0, 2, 4, 6, 8, 6, 4...
I was using Python 3.6 at the time.
Here is a simple and straightforward solution that does not require any imports:
index = 0
constant = 100
while True:
print(index)
if index == constant:
step = -1
elif index == 0:
step = 1
index += step

Combining two lists into one list where they share the same values and removing duplicates using list comprehensions

I am trying to combine two lists:
One holds Square numbers.
The other stores Pentagonal Numbers.
def pentaSquares():
l = []
n = 0
squares = lambda x: [x*x for x in range(n)]
penta = lambda y: [y*(3*y-1)//2 for y in range(n)]
while l.index < 4:
l = [i for i in squares for j in penta if squares == penta]
n = n+1
return l
I must merge these lists using List Comprehensions where their values match until there are 4 elements in the list.
If somebody could point me in the right direction, that would be much appreciated.
I am currently getting this error: TypeError: unorderable types: builtin_function_or_method() < int()
Using a pair of generators should give you this answer without taking up all the memory in the world. This should work nicely (though perhaps take a very long time) for any resultant list size.
import itertools
squares = (x*x for x in itertools.count(0))
pentas = (y * (3*y-1) // 2 for y in itertools.count(0))
results = []
cur_s, cur_p = next(squares), next(pentas)
# prime the pump
while len(results) < 4:
if cur_s == cur_p:
results.append(cur_s)
# success
# advance the generator with the smaller current result
if cur_s > cur_p:
cur_p = next(pentas)
else:
cur_s = next(squares)
There's no reason to use list comprehensions for this task, but if you had to you should use the list -> set and set intersection approach in cricket_007's now-deleted answer
for n in range(itertools.count(0)):
squares = [x * x for x in range(n)]
pentas = [y * (3*y-1) // 2 for y in range(n)]
result = set(squares).intersection(set(pentas))
if len(result) >= 4:
break
def pentaSquares(n):
squarlist=[x*x for x in range(n)]
pentalist=[y * (3*y-1) // 2 for y in range(n)]
l=[x for x in squarlist if x in pentalist]
return l
>>> pentaSquares(10000)
[0, 1, 9801, 94109401]
EDIT 1 O.P Satisfaction
def pentaSquares(n):
squarlist=[]
pentalist=[]
squares = lambda x:x*x
penta = lambda y:y*(3*y-1)//2
for i in range(n):
squarlist.append(squares(i))
pentalist.append(penta(i))
l=[x for x in squarlist if x in pentalist]
if l < 4:
print('there are less than 4 values, input larger n')
return l

How do I preallocate a matrix of any size and fill it in a for loop in Python

I am newer to Python (used to work in IDL and MATLAB) and am trying to get used to how Python "indexes" arrays. I am trying to initiate a 7x13 matrix and fill it in a for loop:
def func(theta2, phi2):
sph = [[[] for l in range(6)] for m in range(12)]
for l in range(0,6):
for m in range(-6,6):
sph[l,m]=np.real(np.conjugate(sph_harm(m,l,phi2,theta2))*np.sin(theta2))
return sph
f = func(np.pi/4,np.pi/4)
This results in this error: "TypeError: list indices must be integers, not tuple
". If I remove the [l,m] index on my variable "sph", I only get a 1x1 array output instead of the desired 7x13 array output.
I have also tried removing the for loops all together and combining it all into one line:
def func(theta2, phi2):
sph = [[np.real(np.conjugate(sph_harm(m,l,phi2,theta2))*np.sin(theta2)) for l in range(6)] for m in range(-6,6)]
return sph
f = func(np.pi/4,np.pi/4)
This resulted in a list of 12, 1x6 arrays which is also not what I wanted.
This is similar to this post: How to initialize a two-dimensional array in Python? but I can't seem to figure out how to correctly implement what was suggested in the responses here.
How would I go about fixing this issue?
Because question title doesn't mention NumPy (but question body and all answers does) I just put below the code in "pure" Python for anybody who get here from Google and doesn't need NumPy solutions.
m = [[0] * col_count for _ in range(row_count)]
Result:
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
In NumPy you should not write loops.
Define your m and l, here called x and y,as arrays:
zeros = np.zeros((7, 13))
x = zeros + np.arange(7).reshape(7, 1)
y = zeros + np.arange(-6, 7)
Write your function sph_harm() so that it works with whole arrays. For example:
def sph_harm(x, y, phi2, theta2):
return x + y * phi2 * theta2
Now, creating your array is much simpler, again working with whole arrays:
def func(theta2, phi2):
zeros = np.zeros((7, 13))
x = zeros + np.arange(7).reshape(7, 1)
y = zeros + np.arange(-6, 7)
return np.real(np.conjugate(sph_harm(x, y, phi2, theta2)) * np.sin(theta2))
f = func(np.pi/4, np.pi/4)
change
sph[l,m]=np.real(np.conjugate(sph_harm(m,l,phi2,theta2))*np.sin(theta2))
into
sph[l][m]=np.real(np.conjugate(sph_harm(m,l,phi2,theta2))*np.sin(theta2))
and
>>> list(range(6))
[0, 1, 2, 3, 4, 5]
>>> list(range(0,6))
[0, 1, 2, 3, 4, 5]
range(n) gives you a list from 0 to n-1

Categories