understanding range in python for loop - python

The program below is finding prime numbers in a given range. for the noprimes list comprehension part, why do we have 3 parameters in range?
noprimes = [j for i in range(2, 8) for j in range(i*2, 50, i)]
primes = [x for x in range(2, 50) if x not in noprimes]
print prime
and what is i doing there?

See the docs:
range([start], stop[, step])
When comparing it to a for(..; ..; ..) loop e.g. in C the three arguments are used like this:
for(int i = start; i != stop; i += step)
There are also good examples in the docs:
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(1, 11)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> range(0, 30, 5)
[0, 5, 10, 15, 20, 25]
>>> range(0, 10, 3)
[0, 3, 6, 9]
>>> range(0, -10, -1)
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
>>> range(0)
[]
>>> range(1, 0)
[]

For range(), the basic idea is that it generates a sequence of items for you. See this as reference http://docs.python.org/library/functions.html#range :
format: range([start], stop[, step])
In the meantime here is some basic explanation, easiest example:
range(5)
will generate numbers in the range starting at 0 (default start value) and go up to but not including 5, by increments of 1 (default value), so
In [1]: range(5)
Out[1]: [0, 1, 2, 3, 4]
You can specify additional parameters to range, such as the starting value, the ending value and also the stepvalue. So range(startval, endval, stepval). Notice that endval is not included in the sequence that is generated.
range(0, 5, 1)
is equivalent to
range(5)
To generate all even numbers between 0 and 20 you could do for instance
range(0, 21, 2)
Note that prior to Python 3 range generates a list and xrange generates the number sequence on demand.
In your specific code are using list comprehensions and range. It might be easier to understand the algorithm and the role of the for loops with range by eliminating the list comprehension temporarily to get a clearer idea. List comprehension is a powerful and efficient construct and should definitely be used if you plan on keeping the original code though.
#noprimes = [j for i in range(2, 8) for j in range(i*2, 50, i)]
noprimes = []
for i in range (2, 8):
for j in range (i*2, 50, i):
noprimes.append(j)
# primes = [x for x in range(2, 50) if x not in noprimes]
primes = []
for x in range(2, 50):
if x not in noprimes:
primes.append(x)

Basically you're stepping by i in non-primes to generate multiples of i (any multiple obviously a non-prime). i is in range(2,8) i.e. [2, 3, 4, 5, 6, 7] because for primes till 50 you only need to eliminate multiples of numbers until sqrt(50) which is 7 (approximately).
If a nested list comprehension is confusing, try breaking it down into steps for easier understanding.
>>> [j for i in [2] for j in range(i*2, 50, i)]
[4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48]
>>> [j for i in [3] for j in range(i*2, 50, i)]
[6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48]
BTW, also look around online for better prime number algorithms. This one algorithmically very poor.

The three parameters of range were explained by ThiefMaster. about the code - code looks ok to me. The only problem seems to be the print prime line. maybe you should add
for prime in primes :
print prime

Related

Converting a list into a matrix by diagonally filling up the elements

I have a sorted list which looks as below:
mylist = [-2, -2, 1, 1, 4, 4, 3, 3, 3]
The list is sorted in ascending order based upon the number of times it appears. In case of a tie, the list is sorted based upon the values.
I need to convert this list into a square matrix of equal chunks(3*3 in this case) such that the numbers are placed "diagonally" starting from the bottom right corner.
The general case is to divide the list in equal chunks.
Desired Output:
res = [[3, 3, 4],
[3, 4, 1],
[1, -2, -2]]
I have written the below code but still not able to get the desired output:
def create_matrix(lst, n):
for i in range(0, len(lst), n):
print(i)
yield lst[i: i+n]
m = create_matrix(mylist, 3)
print(list(m))
One solution could be to place pairs in queue/stack and then pop as needed.
I assume you want to iterate the output matrix this way (NxN = 5x5 example, 0->24 order):
[[ 0, 1, 3, 6, 10],
[ 2, 4, 7, 11, 15],
[ 5, 8, 12, 16, 19],
[ 9, 13, 17, 20, 22],
[14, 18, 21, 23, 24]]
For each cell, the coordinates (i,j) have their sum equal to the number of the diagonal (k) from top-left to bottom right (2*N-1 diagonals in total)
For the N first diagonals, the first item has i=0, the following ones i=k-N where k is the diagonal number.
The last item has i=k with a maximum of N.
j = k-i
This gives us the following algorithm to iterate the cells in order:
import math
mylist = [-2, -2, 1, 1, 4, 4, 3, 3, 3]
N = int(math.sqrt(len(mylist))) # 3
out = [[None for _ in range(N)]
for _ in range(N)]
r = reversed(mylist)
for k in range(2*N-1):
start = 0 if k<N else k-N+1
stop = min(k, N-1)
for i in range(start, stop+1):
out[i][k-i] = next(r)
print(out)
Output:
[[3, 3, 4],
[3, 4, 1],
[1, -2, -2]]
alternative approach
If you want to tackle the problem the other way around and generate the items in order of the rows, then columns, you can use a generator:
def yield_diag(lst, N):
for I in range(N):
# sum of successive diagonal lengths
i = I*(I+1)//2
j = 0
for J in range(I, N+I):
# step between columns
j += min(J, N)-max(0, J-N+1)
yield lst[i+j]
# 5x5 example
list(yield_diag(list(range(25)), 5))
# [0, 1, 3, 6, 10, 2, 4, 7, 11, 15, 5, 8, 12, 16, 19, 9, 13, 17, 20, 22, 14, 18, 21, 23, 24]
OP example:
list(yield_diag(mylist[::-1], 3))
# [3, 3, 4, 3, 4, 1, 1, -2, -2]
As 2D:
N = 3
it = yield_diag(mylist[::-1], N)
[[next(it) for _ in range(N)]
for _ in range(N)]
Output:
[[3, 3, 4],
[3, 4, 1],
[1, -2, -2]]
Here's some code to wrap a list backwards up a matrix, which seems to solve this case. Also, your question doesn't make any sense. Diagonally? And how is that list sorted?
def create_matrix(lst, n):
ret = [[0 for i in range(n)] for i in range(n)]
for i in range(n*n-1,-1,-1):
ret[i//n][i%n] = lst[n*n-1-i]
return ret
Edit: That doesn't work. I don't think anyone knows what you mean by diagonally.

Finding min, max, and avg of every N-values in a list

I have just started learning Python for data Analytics and I found a simple but unsolvable for me problem. I want to find min, max and average of every N-numbers in a list. And I am stuck on the best and fastest way to do it. Do I have to split the list on sublists and then join the output or is there a faster way?
For example:
I have a list of
lst = [6, 6, 5, 6, 7, 11, 10, 9, 9, 8, 13, 13, 13, 13, 14]
and I want to find the min, max and avg of every 5 numbers and append them on seperatily lists like:
avg = [6.0, 9.4, 13.2]
min = [5.0, 8.0, 13.0]
max = [7.0, 11.0, 14.0]
If i understand you correctly:
Here is an example
lst = [6, 6, 5, 6, 7, 11, 10, 9, 9, 8, 13, 13, 13, 13, 14]
def Average(lst):
return sum(lst) / len(lst)
new = [Average(lst[i:i+5]) for i in range(0, len(lst), 5)]
UPDATE: for all:
[[Average(lst[i:i+5]) for i in range(0, len(lst), 5)], [max(lst[i:i+5]) for i in range(0, len(lst), 5)], [min(lst[i:i+5]) for i in range(0, len(lst), 5)]]
however write them seperately ;)
Here is a simple solution for the example you gave:
lst = [6, 6, 5, 6, 7, 11, 10, 9, 9, 8, 13, 13, 13, 13, 14]
lavg = [sum(lst[i:i+5])/len((lst[i:i+5])) for i in range(0, len(lst), 5)]
lmin = [min(lst[i:i+5]) for i in range(0, len(lst), 5)]
lmax = [max(lst[i:i+5]) for i in range(0, len(lst), 5)]
print(lavg)
print(lmin)
print(lmax)
output is :
[6.0, 9.4, 13.2]
[5, 8, 13]
[7, 11, 14]
I'd do it this way: find out the amount of "slices" your list contains, round it up to the bigger number, and then run a loop in range of this value, using incremented variable to assess the indexes of each slice. And within each iteration of the loop, apply the functions you need - max(), min() or whatever.
If needed, you can append them to the pre-created lists, or simply print them out, as you wish
lst = [6, 6, 5, 6, 7, 11, 10, 9, 9, 8, 13, 13, 13, 13, 14]
slices = round(len(lst) / 5)
slicer = 0
for i in range(slices):
max_val = max(lst[slicer:slicer + 5])
slicer +=5
print(max_val)
Note: I do not generally believe in offering answers for questions that show little effort, but other answers have been offered, and this one only builds on those existing answers.
The other answers seem oriented toward iterating over groups of 5 elements for each stat you wish to accumulate.
For large datasets this will be inefficient. What if you want to generate them all on a single pass?
stats = [[], [], []]
for sublist in (lst[i:i+5] for i in range(0, len(lst), 5)):
stats[0].append(sum(sublist) / len(sublist))
stats[1].append(min(sublist))
stats[2].append(max(sublist))
stats = [tuple(stat) for stat in stats]
If we further wanted to generalize this, let's call the function applyn because it applies n functions to an iterable item and returns the results as a list of tuples.
def applyn(iter, *funs):
results = [[] for _ in funs]
for x in iter:
for i, f in enumerate(funs):
results[i].append(f(x))
return [tuple(x) for x in results]
Now, we can write:
stats = applyn((lst[i:i+5] for i in range(0, len(lst), 5)),
lambda x: sum(x) / len(x),
min, max)
And we get:
[(6.0, 9.4, 13.2), (5, 8, 13), (7, 11, 14)]

Select every nth element in list, with n rational/non-integer

int_list = list(range(1000)) # list [0, 1, 2, 3, 4, ..., 999]
Now I want to select every nth element from this list.
Assume n to be rational. Here: n = 7/3 = 2.333...
The size of the new list should therefore be around 42,85% (1/n) of the original one.
But I don't want the elements to be selected randomly. The gap between the selected elements should be similar, but doesn't need to be always exactly the same.
The result may differ by your algorithm used and the algorithm is up to you as long as it fulfils the requirements. But to answer the comment, here is an example of what the result could look like:
int_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
out_list = [0, 2, 4, 8, 10]
The size of out_list is 45.4545...% of the int_list, which isn't 42,85%, but it's the nearest it could get.
Scaling the range by the denominator, stepping with the numerator, and scaling back down by dividing by the denominator:
>>> [i // 3 for i in range(0, 3 * 1000, 7)]
[0, 2, 4, 7, 9, 11, 14, 16, 18, 21, 23, 25, 28, 30, 32, 35, ..., 991, 994, 996, 998]
Your example is a range, not a list. If you really do have a list, then simply use my numbers as indices, i.e., instead of i // 3 do a[i // 3].
A rather generic approach where the list doesn't have to consist of the int range and where the rational value could even be less than one:
N = 7
D = 3
k = 0
result = []
for i in int_list:
k += D
while k >= N:
result.append(i)
k -= N
A naive take would be
from math import floor
def every_n(lst: list, n: int) -> list:
l = []
i = 0
while i < len(lst):
l.append(lst[floor(i)])
i += n
return l
>>> every_n(range(1000), 7/3)
[0, 2, 4, 7, 9, 11, 14, 16, 18, 21, 23, 25, 28, 30, 32, 35, ..., 991, 994, 996, 998]

How to find the difference of two lists including how they're ordered

I've set up some code here:
L1 = [4, 1, 4, 2, 6, 7, -1, 15, 1, 18, 42, 51]
L2 = [4, 1, 4, 3, 6, 10, -1, 15, 2, 18, 42, 51, 92, 16, 41]
d = 0
d = [i for i in L1 if i not in L2]
L3 = abs(len(L1)-len(L2))
print("The lists have", len(d) + L3, "differences.")
And the output is "The lists have 4 differences."
But I'm supposed to get 6 differences because the order of the numbers in the lists matter. And if one list is longer than another, how much longer it is is also supposed to be considered a difference.
If index 1 has "2" in list 1, and index 1 has "3" in list 2, that's supposed to be considered a difference even if both lists have "2" and "3".
I just really don't know how to make it do that.
I'm going to solve this for you, but you can do this stuff -- give yourself a chance and think through what it is you need to do.
# initializations
L1 = [4, 1, 4, 2, 6, 7, -1, 15, 1, 18, 42, 51]
L2 = [4, 1, 4, 3, 6, 10, -1, 15, 2, 18, 42, 51, 92, 16, 41]
# get the min and max sizes
min_len, max_len = min(len(L1), len(L2)), max(len(L1), len(L2))
# initialize differences to the absolute difference in length
differences = max_len - min_len
# for every ith item in the list, increment differences by 1 if it isn't equal
# to the ith item in the other list
for i in range(0, min_len):
if L1[i] != L2[i]:
differences += 1
# print it out
print("The lists have", differences, "differences.")
Ask yourself some questions about what I wrote -- why did I do the min and max first? Why, in the for loop did I use the min_len as the upper bound in the range? ;)

Python: simple way to increment by alternating values?

I am building a list of integers that should increment by 2 alternating values.
For example, starting at 0 and alternating between 4 and 2 up to 20 would make:
[0,4,6,10,12,16,18]
range and xrange only accept a single integer for the increment value. What's the simplest way to do this?
I might use a simple itertools.cycle to cycle through the steps:
from itertools import cycle
def fancy_range(start, stop, steps=(1,)):
steps = cycle(steps)
val = start
while val < stop:
yield val
val += next(steps)
You'd call it like so:
>>> list(fancy_range(0, 20, (4, 2)))
[0, 4, 6, 10, 12, 16, 18]
The advantage here is that is scales to an arbitrary number of steps quite nicely (though I can't really think of a good use for that at the moment -- But perhaps you can).
You can use a list comprehension and the modulus operator to do clever things like that. For example:
>>> [3*i + i%2 for i in range(10)]
[0, 4, 6, 10, 12, 16, 18, 22, 24, 28]
l = []
a = 0
for i in xrnage (N) :
a += 2
if i&1 == 0 :
a+=2
l.append (a)
Looks simple enough to me.
This could be solution that is flexible and work for any range.
def custom_range(first, second, range_limit):
start , end = range_limit
step = first + second
a = range(start, end, step)
b = range(first, end, step)
from itertools import izip_longest
print [j for i in izip_longest(a,b) for j in i if j!=None]
custom_range(4,2,(0,19))
custom_range(6,5,(0,34))
Output:
[0, 4, 6, 10, 12, 16, 18]
[0, 6, 11, 17, 22, 28, 33]
1 Generate a range of numbers from 0 to n with step size 4
2 generate another range of numbers from 0 to n with step size 6
3 Combine both the list and sorted. Remove duplicates
>>> a = range(0,20,4)
>>> a
[0, 4, 8, 12, 16]
>>> b = range(0,20,6)
>>> c = sorted(a + b)
>>> b
[0, 6, 12, 18]
>>> c
[0, 0, 4, 6, 8, 12, 12, 16, 18]
>>> c = list(set(c))
>>> c
[0, 4, 6, 8, 12, 16, 18]

Categories

Resources