Most efficient way to make range between 2 numbers - python-3.x

Right now i'm using the simple for loop,
p1 += ([(x,i) for i in range(j,y+1)])
Just an example from my code, i'm making ranges of tuples, for example
j = 1, y= 9998 , x=1
So I should get (1,1),(1,2)......(1,9998) It's a list of tuples.
The loop will take a lot of time if we having big number.
There is a way to improve without using libraries.

You can use the zip(iter1, iter2, ...) function to create an iterator wich yields a tuple consisting of the elements in the iterables passed in the argument.
Use itertools.cycle() to provide the constant element in your tuples.
You can save time if you use the iterator returned by the zip() (i.e not making it a list()), but you can only use that once. If the iterator is exhausted you need to create a new one, but that is ridiculously fast.
That way is about 30% faster, on Python 3.7.1
Here is the code to test it yourself:
import timeit
from itertools import cycle
x = 5
j = 1
y = 10000
def method1():
return [(x, i) for i in range(j, y + 1)]
cycling_x = cycle([x])
def method2():
return list(zip(cycling_x, range(j, y + 1)))
print(timeit.timeit("method1()", number=10000, globals=globals()))
print(timeit.timeit("method2()", number=10000, globals=globals()))
>>> 10.956179626
>>> 7.571100585999998

The zip() function combines two iterators into a series of tuples. itertools.repeat() creates an iterator that always yields the same object.
import itertools
zip(itertools.repeat(x), range(j, y+1))
This will return a lazy iterator, which you can loop over once (e.g. for a, b in zip(...): ...). If you want all of the tuples collected into a list which you can access repeatedly and out of order, call list() on the result of zip(...).

Related

what is the use of iterator in python ? already we have looping

what is the use of iterator in python ? instead of that we can loop right.
Below is my code which calculates the time for both operations
*
import time
start1 = time.time()
a=list(range(1,10000000))
myiter=iter(a)
while True:
try:
next(myiter)
except StopIteration:
break
end1 = time.time()
print(end1-start1)
print("-"*40)
start = time.time()
a=list(range(1,10000000))
for i in a:
pass
end = time.time()
print(end-start)
please post your feedback.
Thanks in advance
Output :
5.00525975227356
2.208195924758911
Short Answer
Here is an example of how you can improve your code with iterators:
// WITHOUT ITERATORS
hours_worked = 0
for event in events:
if event.is_billable():
hours_worked += event.duration
// WITH ITERATORS
// create an iterators with the duration of the billable event.
billable_times = (event.duration for event in events if event.is_billable())
hours_worked = sum(billable_times)
With the iterators we have named something that was unnamed before and we can now use the sum function. Naming things make our code more descriptive and more readable.
Learn more here: Python Basics: Iteration, Iterables, Iterators, and Looping
Long Answer
Python doesn't have the same for loops as other programming languages. You probably know that in JavaScript a "for loop" is written like this:
let myList = ["apple", "banana", "blueberry"];
for (let i = 0; i < myList.length; i += 1) {
print(myList[i])
}
And like you know this is the Python "for loop":
myList = [1, 2, 3, 5, 7]
for n in myList:
print(n)
As you can see, in Python there is no index initializing, incrementing and checking. In Python, myList is called an iterable it's everything you can loop over with a for loop.
Sequences are one type of iterable but with additional features like indexing, length and they can be sliced. For example: Strings, Tuples and Lists are iterables and sequences, but dictionaries are just iterables. (You can't do this with a dictionary myDict[1]).
In Python, we cannot manually loop over every iterable by using indexes, it only works with sequences.
So how do Python "for loops" work if they can use indexes? They user the power of Iterators. Iterators are the things that power iterables, you can get one from every iterable, it's what you are doing when with iter(a) in your code.
You can think of iterators as Pez dispensers that cannot be reloaded. You can take Pez out, but once a Pez is removed it can't be put back, and once the dispenser is empty, it's useless.
Source: Loop better: A deeper look at iteration in Python: Article
So under the hood, in Python, a for loop is working something like this :
def for(iterable, action)
iterator = iter(iterable)
looping_finished = false
while not looping_finished:
try:
item = next(iterator)
except StopIteration:
looping_finished = true
else:
action(item)
But why do we care about iterators? Because in Python it's common to work with an iterator directly.
Here is a basic example of a Python generator
my+1Generator (n+1 for n in numbers)
Generators are iterators, it means that you can do this: next(my+1Generator) and you can also loop over generators like this:
for n in my+1Generator:
print(n)
If you can loop over it, it's an iterable. So, generators are iterators but also iterables.
And, surprise, it's the same for all iterators. You can actually do this:
numbers = [1, 2, 3]
iterator1 = iter(numbers)
iterator2 = iter(iterator1)
print (iterator1 is iterator2) // Output: "True"
Iterators have no length and can't be indexed. For example: enumerate, reverse, zip and map are iterators in Python. And the most important things about iterators is their laziness. They delay their work until you ask for the next item.
You can create an iterator yourself. Here is an example:
class add1_all:
def __init__(self, numbers):
self.numbers = iter(numbers)
def __next__(self):
return next(self.numbers) + 1
def __iter__(self):
return self
but usually an iterator is made with a generator function:
def add1_all(numbers):
for n in numbers:
yield n+1
Those two code samples are equivalent and work the same way. yield allows us to pause the execution of the add1_all function until the next function is called on it.
Here is an example of how you can improve your code with iterators:
// WITHOUT ITERATORS
hours_worked = 0
for event in events:
if event.is_billable():
hours_worked += event.duration
// WITH ITERATORS
// create an iterators with the duration of the billable event.
billable_times = (event.duration for event in events if event.is_billable())
hours_worked = sum(billable_times)
Sources:
Loop better: a deeper look at iteration in Python: Video
Python documentation #Iterators
Loop better: A deeper look at iteration in Python: Article
Python Basics: Iteration, Iterables, Iterators, and Looping

how to return a list of all integers that occur at least three times using nested loop and/or dictionary?

how to return a list of all integers that occur at least three times using nested loop and/or dictionary? I have used .count() to find number of value in the list, but how can I use nested loop or using dictionary to do the same performance as this function.
x = 1,10,-5,10,10,-1,22,3,1,3,7,-5,22,-5,3,10,5
triples(x)
[3, 10, -5]
def triples(entries):
out=[]
for val in entries:
if entries.count(val) >= 3 and int(val) not in out:
out.append(int(val))
return out
Just use a collections.Counter:
from collections import Counter
def triples(entries):
return [k for k, v in Counter(entries).items() if v >= 3]
This has linear time complexity unlike your repeated count approach or the nested loops you suggest.

What's most efficient: a= len(x)*[""] or a= ["" for i in x]?

I want to have a list with multiple instances of the same "dummy" data that I'll replace afterwards (with a[index] = new data).
I came through with two ways of doing this.
a= len(x)*[""]
and
a= ["" for i in x]
How can I tell which one will be the best? Is there any difference in how they run?
Both seem to yield the exact same result.
I tried looking this up in the documentation, stack overflow and google in general.
Of course, you can use the library timeit to check for perfomance speed:
from timeit import timeit
x = [0, 1, 2, 3, 4, 5]
def usingLen():
a = len(x)*[""]
def usingListComprehension():
a = ["" for i in x]
print(timeit("usingLen()", setup="from __main__ import usingLen"))
print(timeit("usingListComprehension()", setup="from __main__ import usingListComprehension"))
The output is:
0.373054536
0.554289093
That means, executing 1 million of times len(x)*[""] takes 0.37 seconds while executing a 1 million of times ["" for i in x] takes 0.55 seconds. Using len seems faster.
I can't exactly say you why, but I think it has to do with the iteration of the comprehension list. len() just count the size of the array and then multiply the item "", while the list comprehension must iterate for each element of the array (which you don't need) in order to create each "" item, which sounds as a heavier task.
The second iterates for each element of the array and creates per each element an "" item. While the first creates the length of the array number of times "" item, in no moment check what is each item.

Getting a list item with the max evaluation in a list of tuples in Python

Given this list of tuples:
my_tuples = [(1,2), (3,4)]
and the following evaluation function:
def evaluate(item_tuple):
return item_tuple[0] * 2
Question: how can I get the list item (tuple) that has the highest evaluation value? (I'm guessing I can use a list comprehension for this)
def max_item(tuples_list, evaluation_fn):
'''Should return the tuple that scores max using evaluation_fn'''
# TODO Implement
# This should pass
assertEqual((3,4), max_item(my_tuples, evaluate))
Correct me if I'm wrong, you want the list of tuples sorted by the result of multiplying one of the values inside the tuple with x (in your example above it would be the first value of the tuple multiplied by 2).
If so, you can do it this way:
from operator import itemgetter
sorted(l, key=itemgetter(0 * 2), reverse=True)
I managed to do it this way:
def max_item(tuples_list, evaluation_fn):
zipped = zip(map(evaluation_fn, tuples_list), tuples_list)
return max(zipped, key=lambda i:i[0])[1]
I don't know if there's a simpler (more pythonic?) way to solve it though.
Edit
I figured how I could use a list comprehension to make it more succinct/readable:
def max_item(tuples_list, evaluation_fn):
return max([(evaluation_fn(i), i) for i in tuples_list])[1]

When is map() necessary?

Given the following:
(I) a = map(function, sequence)
(II) a = [function(x) for x in sequence]
When would I need to use (I)? Why choose a map object over a list when the latter is subscriptable and IMO more readable?
Also, could someone explain line 6 of the following code (Python 3):
>>>import math
>>>a = map(int,str(math.factorial(100)))
>>>sum(a)
648
>>>sum(a)
0
Why is the sum of the map object changing?
When would I need to use (I)? Why choose a map object over a list when the latter is subscriptable and IMO more readable?
map was introduced in Python 1.0, while list comprehension was not introduced until Python 2.0.
For Python 2+, you never need to use one or the other.
Reasons for still using map could include:
preference. You prefer list comprehension, not everyone agrees.
familiarity. map is very common across languages. If Python's not your native language, "map" is the function you'll look up.
brevity. map is often shorter. Compare map and lambda f,l: [f(x) for x in l].
I is an iterator -- it creates a stream of values which then vanish. II is a list -- it lasts for a while and has lots of features, like len(mylist) and mylist[-3:].
The sum changes because the iterator vanishes after you use it.
Use lists and list comprehensions. If you process tons of data, then iterators (and generators, and generator comprehensions) are awesome, but they can be confusing.
Or, use an iterator and convert into a list for further processing:
a = list( map(int,str(math.factorial(100))) )
From the docs:
Apply function to every item of iterable and return a list of the results. If additional iterable arguments are passed, function must take that many arguments and is applied to the items from all iterables in parallel...
The sum changes to 0 because the iterator is iterated, so it becomes nothing. This is the same concept with .read() (Try calling x = open('myfile.txt'), and then type print x.read() twice.)
In order to preserve the iterable, surround it with list():
>>> import math
>>> a = map(int,str(math.factorial(100)))
>>> sum(a)
648
>>> sum(a)
0
>>> a = list(map(int,str(math.factorial(100))))
>>> sum(a)
648
>>> sum(a)
648
Example from the docs:
>>> seq = range(8)
>>> def add(x, y): return x+y
...
>>> map(add, seq, seq)
[0, 2, 4, 6, 8, 10, 12, 14]

Resources