How can I make a combined power function faster? - python

I made this function pretty quickly to find the combined powers of everything in a list, but I'm pretty sure there is a way to make it faster. It returns a 2 item list, with the combined powers and the list formatted for export.
#The input for this function is a list of numbers (but string data type)
def find(x):
total = int(x[0])
for i in range (1,len(x)):
total = total ** int(x[i])
value = [total,'^'.join(x)]
return value

This will calculate the values faster than what you currently have:
import functools
import operator
l = [2, 3, 4]
functools.reduce(operator.pow, l)
## 4096
If you want to show the chain of values in the list too, as in the original post, you can define a function e.g. like this one:
def func(vals):
vals_string = '^'.join(str(val) for val in vals)
total = functools.reduce(operator.pow, vals)
return [total, vals_string]
Usage example:
l = [2, 3, 4, 5]
result = func(l)
print(result)
## [1152921504606846976, '2^3^4^5']

You want to avoid doing a stack of exponentiations, as those are expensive. Multiplication is cheaper, so you can save computing power by multiplying all the values to the right of the first one, and then raising the first to that power.
from functools import reduce
from operator import mul
from typing import List
def find(x: List[int]) -> int:
return x[0]**reduce(mul, x[1:], 1)
You could also do
def find(i: int, *updog: int) -> int:
return i ** reduce(mul, updog, 1)
and call it like find(*[2,1,2,3,4]) or find(2, 1, 2, 3).
Either way, this uses the function signature to provide a stricter guarantee that the initial value is set, rather than having undefined behavior if the list is empty.

You can use NumPy's power.accumulate() to have cumulative exponents. However, you will soon run into large numbers for a large input list. The [-1] here gives you the cumulative of all the elements
import numpy as np
def find(x):
total = np.power.accumulate(x)[-1]
value = [total,'^'.join(map(str, inp))]
return value
x = np.array([2,3,4,5])
print (find(x))
# [1152921504606846976, '2^3^4^5']

You can use functools.reduce():
from functools import reduce
import operator
reduce(operator.pow, list)
What reduce() does is it applies the first parameter to every element in the second.

Related

how to find mode of a list that favors the lesser value when 2 values appear equally

def mode(list):
modelist = max([list.count(x) for x in list])
return [i for i in list if list.count(i) == modelist][0]
Essentially, the code above works in most cases. However, I want the code to favor the lesser value when there are two equal values.
so if the input list was [5,2,2,5] I want it to print 2 instead of printing 5
Is there a reason you don't want to use multimode from the statistics library?
from statistics import multimode
print (min(multimode([2,5,2,5,3,3])))
You can return the min of the list you already have:
def mode(lst):
modelist = max(lst.count(x) for x in lst)
return min(i for i in lst if lst.count(i) == modelist)
print(mode([5, 2, 2, 5])) # 2
The code as is now has quite a large time complexity; I guess it is O(n^2). To reduce it, you can set up a counter (e.g., by using collections.Counter):
from collections import Counter
def mode(lst):
return min(x[1] for x in Counter(lst).most_common(1))
With a long list like [5, 2, 2, 5] * 5000, time spent is like 7.429 vs 0.002 seconds (on my machine).

Trying to add specific values from a matrix without using numpy

I am supposed to create a function that adds the absolute value of numbers that are larger than a certain number without using any python modules to do it. I wrote this:
def MatrixSumLarge(vals,large):
sum=0
sum1=0
sum2=0
i=0
#looking at the rows of the matrix
for i in range(len(vals[i])):
if abs(vals)>=large:
sum1=sum1+i
#lookinng at the columns of the matrix
for j in range(0,len(vals[j])):
if abs(vals[j])>=large:
sum2=sum2+vals[j]
sum=sum1+sum2
return(sum)
vals=[[1,-2.5,7,4],[-8,9,2,-1.5],[-12,7.5,4.2,11]]
# setting value of large
large = 7.1
#to print the final answer
print('b) Sum of Large Values = ',MatrixSumLarge(vals,large))
And got the error:
TypeError: bad operand type for abs(): 'list'
You vals is a list of list, so vals[i] is a list. However, abs() cannot be applied on a list, it should be applied on a number (floating number, integer, or complex number), see the official document here. You can for example, add an extra loop to add up the element in the row, like sum(abs(x) for x in vals[i] if x >= 2)
You can change 2 to the number you need.
Overall, your function could look something like this
def f(mat, v):
row_sums = [sum(abs(x) for x in row if x >= v) for row in mat]
return sum(row_sums)
From your question, it's not very clear to me whether you wanna
compare abs(x) to v or x to v
sum up abs(x) or x
But you should be able to adjust the code above to suit your need.
There is also a python module that helps you to flatten list of list. The function is called chain.from_iterable. So the above code could also be written like this
from itertools import chain
def f(mat, v):
sums = [sum(abs(x) for x in chain.from_iterable(mat) if x >= v]
return sum(sums)

Finding the sum of a nested list of ints

import math
lists = [1,[2,3],4]
total = 0
for i in range(len(lists)):
total += sum(i)
print(total)
I want it to print,
>>>10
But throws an error.
I would like it to get it to add all numbers, including the ones within the nested if.
In your program, for i in range(len(lists)) - evaluates to 3 as the lists object has 3 element. and in the loop total += sum(i) it would try to do a int + list operation, which results in an error. Hence you need to check for the type and then add the individual elements.
def list_sum(L):
total = 0
for i in L:
if isinstance(i, list):
total += list_sum(i)
else:
total += i
return total
This is #pavelanossov 's comment - does the same thing, in a more elegant way
sum(sum(i) if isinstance(i, list) else i for i in L)
You can use flatten function in the compiler.ast module to flatten the list. Then simply sum up all the elements.
>>> lists = [1,[2,3],4]
>>> from compiler.ast import flatten
>>> sum(flatten(lists))
10
EDIT: Only works with Python 2.x
numpy.hstack() function is used to stack the sequence of input arrays horizontally (i.e. column wise) to make a single array which is what we require in OP example
import numpy as np
list1 = [1,[2,3],4]
M = np.hstack(list1)
print(np.sum(M))
gives
10
[Program finished]

From list of integers, get number closest to a given value

Given a list of integers, I want to find which number is the closest to a number I give in input:
>>> myList = [4, 1, 88, 44, 3]
>>> myNumber = 5
>>> takeClosest(myList, myNumber)
...
4
Is there any quick way to do this?
If we are not sure that the list is sorted, we could use the built-in min() function, to find the element which has the minimum distance from the specified number.
>>> min(myList, key=lambda x:abs(x-myNumber))
4
Note that it also works with dicts with int keys, like {1: "a", 2: "b"}. This method takes O(n) time.
If the list is already sorted, or you could pay the price of sorting the array once only, use the bisection method illustrated in #Lauritz's answer which only takes O(log n) time (note however checking if a list is already sorted is O(n) and sorting is O(n log n).)
I'll rename the function take_closest to conform with PEP8 naming conventions.
If you mean quick-to-execute as opposed to quick-to-write, min should not be your weapon of choice, except in one very narrow use case. The min solution needs to examine every number in the list and do a calculation for each number. Using bisect.bisect_left instead is almost always faster.
The "almost" comes from the fact that bisect_left requires the list to be sorted to work. Hopefully, your use case is such that you can sort the list once and then leave it alone. Even if not, as long as you don't need to sort before every time you call take_closest, the bisect module will likely come out on top. If you're in doubt, try both and look at the real-world difference.
from bisect import bisect_left
def take_closest(myList, myNumber):
"""
Assumes myList is sorted. Returns closest value to myNumber.
If two numbers are equally close, return the smallest number.
"""
pos = bisect_left(myList, myNumber)
if pos == 0:
return myList[0]
if pos == len(myList):
return myList[-1]
before = myList[pos - 1]
after = myList[pos]
if after - myNumber < myNumber - before:
return after
else:
return before
Bisect works by repeatedly halving a list and finding out which half myNumber has to be in by looking at the middle value. This means it has a running time of O(log n) as opposed to the O(n) running time of the highest voted answer. If we compare the two methods and supply both with a sorted myList, these are the results:
$ python -m timeit -s "
from closest import take_closest
from random import randint
a = range(-1000, 1000, 10)" "take_closest(a, randint(-1100, 1100))"
100000 loops, best of 3: 2.22 usec per loop
$ python -m timeit -s "
from closest import with_min
from random import randint
a = range(-1000, 1000, 10)" "with_min(a, randint(-1100, 1100))"
10000 loops, best of 3: 43.9 usec per loop
So in this particular test, bisect is almost 20 times faster. For longer lists, the difference will be greater.
What if we level the playing field by removing the precondition that myList must be sorted? Let's say we sort a copy of the list every time take_closest is called, while leaving the min solution unaltered. Using the 200-item list in the above test, the bisect solution is still the fastest, though only by about 30%.
This is a strange result, considering that the sorting step is O(n log(n))! The only reason min is still losing is that the sorting is done in highly optimalized c code, while min has to plod along calling a lambda function for every item. As myList grows in size, the min solution will eventually be faster. Note that we had to stack everything in its favour for the min solution to win.
>>> takeClosest = lambda num,collection:min(collection,key=lambda x:abs(x-num))
>>> takeClosest(5,[4,1,88,44,3])
4
A lambda is a special way of writing an "anonymous" function (a function that doesn't have a name). You can assign it any name you want because a lambda is an expression.
The "long" way of writing the the above would be:
def takeClosest(num,collection):
return min(collection,key=lambda x:abs(x-num))
def closest(list, Number):
aux = []
for valor in list:
aux.append(abs(Number-valor))
return aux.index(min(aux))
This code will give you the index of the closest number of Number in the list.
The solution given by KennyTM is the best overall, but in the cases you cannot use it (like brython), this function will do the work
Iterate over the list and compare the current closest number with abs(currentNumber - myNumber):
def takeClosest(myList, myNumber):
closest = myList[0]
for i in range(1, len(myList)):
if abs(i - myNumber) < closest:
closest = i
return closest
def find_nearest(array, value):
array = np.asarray(array)
idx = (np.abs(array - value)).argmin()
return array[idx]
run it by using
price_near_to=find_nearest(df['Close'], df['Close'][-2])
It's important to note that Lauritz's suggestion idea of using bisect does not actually find the closest value in MyList to MyNumber. Instead, bisect finds the next value in order after MyNumber in MyList. So in OP's case you'd actually get the position of 44 returned instead of the position of 4.
>>> myList = [1, 3, 4, 44, 88]
>>> myNumber = 5
>>> pos = (bisect_left(myList, myNumber))
>>> myList[pos]
...
44
To get the value that's closest to 5 you could try converting the list to an array and using argmin from numpy like so.
>>> import numpy as np
>>> myNumber = 5
>>> myList = [1, 3, 4, 44, 88]
>>> myArray = np.array(myList)
>>> pos = (np.abs(myArray-myNumber)).argmin()
>>> myArray[pos]
...
4
I don't know how fast this would be though, my guess would be "not very".
Expanding upon Gustavo Lima's answer. The same thing can be done without creating an entirely new list. The values in the list can be replaced with the differentials as the FOR loop progresses.
def f_ClosestVal(v_List, v_Number):
"""Takes an unsorted LIST of INTs and RETURNS INDEX of value closest to an INT"""
for _index, i in enumerate(v_List):
v_List[_index] = abs(v_Number - i)
return v_List.index(min(v_List))
myList = [1, 88, 44, 4, 4, -2, 3]
v_Num = 5
print(f_ClosestVal(myList, v_Num)) ## Gives "3," the index of the first "4" in the list.
If I may add to #Lauritz's answer
In order not to have a run error
don't forget to add a condition before the bisect_left line:
if (myNumber > myList[-1] or myNumber < myList[0]):
return False
so the full code will look like:
from bisect import bisect_left
def takeClosest(myList, myNumber):
"""
Assumes myList is sorted. Returns closest value to myNumber.
If two numbers are equally close, return the smallest number.
If number is outside of min or max return False
"""
if (myNumber > myList[-1] or myNumber < myList[0]):
return False
pos = bisect_left(myList, myNumber)
if pos == 0:
return myList[0]
if pos == len(myList):
return myList[-1]
before = myList[pos - 1]
after = myList[pos]
if after - myNumber < myNumber - before:
return after
else:
return before
def takeClosest(myList, myNumber):
newlst = []
for i in myList:
newlst.append(i - myNumber)
lstt = [abs(ele) for ele in newlst]
print(myList[lstt.index(min(lstt))])
myList = [4, 1, 88, 44, 3]
myNumber = 5
takeClosest(myList,myNumber)

How does reduce function work?

As far as I understand, the reduce function takes a list l and a function f. Then, it calls the function f on first two elements of the list and then repeatedly calls the function f with the next list element and the previous result.
So, I define the following functions:
The following function computes the factorial.
def fact(n):
if n == 0 or n == 1:
return 1
return fact(n-1) * n
def reduce_func(x,y):
return fact(x) * fact(y)
lst = [1, 3, 1]
print reduce(reduce_func, lst)
Now, shouldn't this give me ((1! * 3!) * 1!) = 6? But, instead it gives 720. Why 720? It seems to take the factorial of 6 too. But, I need to understand why.
Can someone explains why this happens and a work-around?
I basically want to compute the product of factorials of all the entries in the list.
The backup plan is to run a loop and compute it. But, I would prefer using reduce.
The other answers are great. I'll simply add an illustrated example that I find pretty good to understand reduce():
>>> reduce(lambda x,y: x+y, [47,11,42,13])
113
will be computed as follows:
(Source) (mirror)
The easiest way to understand reduce() is to look at its pure Python equivalent code:
def myreduce(func, iterable, start=None):
it = iter(iterable)
if start is None:
try:
start = next(it)
except StopIteration:
raise TypeError('reduce() of empty sequence with no initial value')
accum_value = start
for x in iterable:
accum_value = func(accum_value, x)
return accum_value
You can see that it only makes sense for your reduce_func() to apply the factorial to the rightmost argument:
def fact(n):
if n == 0 or n == 1:
return 1
return fact(n-1) * n
def reduce_func(x,y):
return x * fact(y)
lst = [1, 3, 1]
print reduce(reduce_func, lst)
With that small revision, the code produces 6 as you expected :-)
Your function calls fact() on both arguments. You are calculating ((1! * 3!)! * 1!). The workaround is to only call it on only the second argument, and pass reduce() an initial value of 1.
From the Python reduce documentation,
reduce(function, sequence) returns a single value constructed by calling the (binary) function on the first two items of the sequence, then on the result and the next item, and so on.
So, stepping through. It computes reduce_func of the first two elements, reduce_func(1, 3) = 1! * 3! = 6. Then, it computes reduce_func of the result and the next item: reduce_func(6, 1) = 6! * 1! = 720.
You missed that, when the result of the first reduce_func call is passed as input to the second, it's factorialized before the multiplication.
Ok, got it:
I need to map the numbers to their factorials first and then call reduce with multiply operator.
So, this would work:
lst_fact = map(fact, lst)
reduce(operator.mul, lst_fact)
You could also implement factorial using reduce.
def factorial(n):
return(reduce(lambda x,y:x*y,range(n+1)[1:]))
Beyond the trivial examples, here is one where I find reduce to be actually quite useful:
Imagine an iterable of ordered int values, often with some runs of contiguous values, and that we'd like to "summarize" it as a list of tuples representing ranges. (Note also that this iterable could be a generator of a very long sequence --another reason to use reduce and not some operation on an in-memory collection).
from functools import reduce
def rle(a, b):
if a and a[-1][1] == b:
return a[:-1] + [(a[-1][0], b + 1)]
return a + [(b, b + 1)]
reduce(rle, [0, 1, 2, 5, 8, 9], [])
# [(0, 3), (5, 6), (8, 10)]
Notice the use of a proper initial value ([] here) for reduce.
Corner cases handled as well:
reduce(rle, [], [])
# []
reduce(rle, [0], [])
# [(0, 1)]
Well, first of all, your reduce_func doesn't have the structure of a fold; it doesn't match your description of a fold (which is correct).
The structure of a fold is: def foldl(func, start, iter): return func(start, foldl(func, next(iter), iter)
Now, your fact function doesn't operate on two elements - it just calculates factorial.
So, in sum, you're not using a fold, and with that definition of factorial, you don't need to.
If you do want to play around with factorial, check out the y-combinator: http://mvanier.livejournal.com/2897.html
If you want to learn about folds, look at my answer to this question, which demonstrates its use to calculate cumulative fractions: creating cumulative percentage from a dictionary of data
Reduce executes the function in parameter#1 successively through the values provided by the iterator in parameter#2
print '-------------- Example: Reduce(x + y) --------------'
def add(x,y): return x+y
x = 5
y = 10
import functools
tot = functools.reduce(add, range(5, 10))
print 'reduce('+str(x)+','+str(y)+')=' ,tot
def myreduce(a,b):
tot = 0
for i in range(a,b):
tot = tot+i
print i,tot
print 'myreduce('+str(a)+','+str(b)+')=' ,tot
myreduce(x,y)
print '-------------- Example: Reduce(x * y) --------------'
def add(x,y): return x*y
x = 5
y = 10
import functools
tot = functools.reduce(add, range(5, 10))
print 'reduce('+str(x)+','+str(y)+')=' ,tot
def myreduce(a,b):
tot = 1
for i in range(a,b):
tot = tot * i
print i,tot
print 'myreduce('+str(a)+','+str(b)+')=' ,tot
myreduce(x,y)

Categories

Resources