What would be the best implementation for multiplying a list of numbers raised to a defined number of exponents.
Currently I have a list of 8 numbers that would be needed to be raised to a power. Every list will always contain 8 numbers and the numbers in the list will always have the exponent of which their index value is.
For example:
List = [1,2,3,4,5,6,7,8]
Power(1,0) + Power(2,1) + Power(3,2) +.... Power(8,7)
However, the issue is what if the list has a none value, how can you carry the exponential increased value without affecting the total sum.
Example:
List = [1,None,3,4,5,6,7,8]
Power(1,0) + (none) + Power(3,2) +.... Power(8,7)
Any ideas of implementation would help.
Maybe something like this could get you started?
import numpy as np
l = [1, None, 3, 4, 5, 6, 7, 8]
# If you need to define the powers
# powers = [0, 1, 2, 3, 4, 5, 6, 7]
powers = np.arange(len(l)) # If powers are always indices
arr = np.array([x if x is not None else 0 for x in l])
arr**powers
# array([ 1, 0, 9, 64, 625, 7776, 117649, 2097152]
(arr**powers).sum()
# 2223276
On second thought, the above would have a problem if you had [None, 1, 2, 3] since 0**0=1. So we should probably go with something like
l = [1, None, 3, 4, 5, 6, 7, 8]
numbers = np.array([x for x in l if x is not None])
powers = np.array([i for i in range(len(l)) if l[i] is not None])
(numbers**powers).sum()
List = [1,None,3,4,5,6,7,8]
result = sum([pow(List[i],i) for i in range(len(List)) if str(List[i]).isdigit()])
Well, you can use list comprehension to only pow numeric values:
index_list = range(len(list))
sum([pow(list[i], i) for i in index_list if str(list[i]).isdigit()])
#Output: 2223276
In here, we sum a list that contains all values powered by an exponent. It will only sum numeric values!
Related
I'm trying my hand at converting the following loop to a comprehension.
Problem is given an input_list = [1, 2, 3, 4, 5]
return a list with each element as multiple of all elements till that index starting from left to right.
Hence return list would be [1, 2, 6, 24, 120].
The normal loop I have (and it's working):
l2r = list()
for i in range(lst_len):
if i == 0:
l2r.append(lst_num[i])
else:
l2r.append(lst_num[i] * l2r[i-1])
Python 3.8+ solution:
:= Assignment Expressions
lst = [1, 2, 3, 4, 5]
curr = 1
out = [(curr:=curr*v) for v in lst]
print(out)
Prints:
[1, 2, 6, 24, 120]
Other solution (with itertools.accumulate):
from itertools import accumulate
out = [*accumulate(lst, lambda a, b: a*b)]
print(out)
Well, you could do it like this(a):
import math
orig = [1, 2, 3, 4, 5]
print([math.prod(orig[:pos]) for pos in range(1, len(orig) + 1)])
This generates what you wanted:
[1, 2, 6, 24, 120]
and basically works by running a counter from 1 to the size of the list, at each point working out the product of all terms before that position:
pos values prod
=== ========= ====
1 1 1
2 1,2 2
3 1,2,3 6
4 1,2,3,4 24
5 1,2,3,4,5 120
(a) Just keep in mind that's less efficient at runtime since it calculates the full product for every single element (rather than caching the most recently obtained product). You can avoid that while still making your code more compact (often the reason for using list comprehensions), with something like:
def listToListOfProds(orig):
curr = 1
newList = []
for item in orig:
curr *= item
newList.append(curr)
return newList
print(listToListOfProds([1, 2, 3, 4, 5]))
That's obviously not a list comprehension but still has the advantages in that it doesn't clutter up your code where you need to calculate it.
People seem to often discount the function solution in Python, simply because the language is so expressive and allows things like list comprehensions to do a lot of work in minimal source code.
But, other than the function itself, this solution has the same advantages of a one-line list comprehension in that it, well, takes up one line :-)
In addition, you're free to change the function whenever you want (if you find a better way in a later Python version, for example), without having to change all the different places in the code that call it.
This should not be made into a list comprehension if one iteration depends on the state of an earlier one!
If the goal is a one-liner, then there are lots of solutions with #AndrejKesely's itertools.accumulate() being an excellent one (+1). Here's mine that abuses functools.reduce():
from functools import reduce
lst = [1, 2, 3, 4, 5]
print(reduce(lambda x, y: x + [x[-1] * y], lst, [lst.pop(0)]))
But as far as list comprehensions go, #AndrejKesely's assignment-expression-based solution is the wrong thing to do (-1). Here's a more self contained comprehension that doesn't leak into the surrounding scope:
lst = [1, 2, 3, 4, 5]
seq = [a.append(a[-1] * b) or a.pop(0) for a in [[lst.pop(0)]] for b in [*lst, 1]]
print(seq)
But it's still the wrong thing to do! This is based on a similar problem that also got upvoted for the wrong reasons.
A recursive function could help.
input_list = [ 1, 2, 3, 4, 5]
def cumprod(ls, i=None):
i = len(ls)-1 if i is None else i
if i == 0:
return 1
return ls[i] * cumprod(ls, i-1)
output_list = [cumprod(input_list, i) for i in range(len(input_list))]
output_list has value [1, 2, 6, 24, 120]
This method can be compressed in python3.8 using the walrus operator
input_list = [ 1, 2, 3, 4, 5]
def cumprod_inline(ls, i=None):
return 1 if (i := len(ls)-1 if i is None else i) == 0 else ls[i] * cumprod_inline(ls, i-1)
output_list = [cumprod_inline(input_list, i) for i in range(len(input_list))]
output_list has value [1, 2, 6, 24, 120]
Because you plan to use this in list comprehension, there's no need to provide a default for the i argument. This removes the need to check if i is None.
input_list = [ 1, 2, 3, 4, 5]
def cumprod_inline_nodefault(ls, i):
return 1 if i == 0 else ls[i] * cumprod_inline_nodefault(ls, i-1)
output_list = [cumprod_inline_nodefault(input_list, i) for i in range(len(input_list))]
output_list has value [1, 2, 6, 24, 120]
Finally, if you really wanted to keep it to a single , self-contained list comprehension line, you can follow the approach note here to use recursive lambda calls
input_list = [ 1, 2, 3, 4, 5]
output_list = [(lambda func, x, y: func(func,x,y))(lambda func, ls, i: 1 if i == 0 else ls[i] * func(func, ls, i-1),input_list,i) for i in range(len(input_list))]
output_list has value [1, 2, 6, 24, 120]
It's entirely over-engineered, and barely legible, but hey! it works and its just for fun.
For your list, it might not be intentional that the numbers are consecutive, starting from 1. But for cases that that pattern is intentional, you can use the built in method, factorial():
from math import factorial
input_list = [1, 2, 3, 4, 5]
l2r = [factorial(i) for i in input_list]
print(l2r)
Output:
[1, 2, 6, 24, 120]
The package numpy has a number of fast implementations of list comprehensions built into it. To obtain, for example, a cumulative product:
>>> import numpy as np
>>> np.cumprod([1, 2, 3, 4, 5])
array([ 1, 2, 6, 24, 120])
The above returns a numpy array. If you are not familiar with numpy, you may prefer to obtain just a normal python list:
>>> list(np.cumprod([1, 2, 3, 4, 5]))
[1, 2, 6, 24, 120]
using itertools and operators:
from itertools import accumulate
import operator as op
ip_lst = [1,2,3,4,5]
print(list(accumulate(ip_lst, func=op.mul)))
I've generated index number list what I do find data using for-loop.
That numbers are as follows.
I want to generate 1 interval index numbers between each given numbers
Int64Index([ 2343, 2913, 5466, 8589, 10151, 11713, 13275, 14837, 15618,7961, 19523, 21624, 21700],dtype='int64')
for e.g) 2343,2344,2345,2346 ......,2911,2912,2913,2914....5464,5465,5466 some thing like that.
Is there any method that i generate 1 interval numbers do not use for-loop?
Try using the builtin range() function.
First you need to determine start and end number using the min() and max() functions.
start_num = min(listr)
end_num = max(listr)
Then we use range() to create our new list
new_listr = list(range(start_num, end_num+1))
The complete sample code:
listr = [2343, 2913, 5466, 8589, 10151, 11713, 13275, 14837, 15618,7961, 19523, 21624, 21700]
start_num = min(listr)
end_num = max(listr)
new_listr = list(range(start_num, end_num+1))
Did you try:
>>> range(1,10)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(1,10,1)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(1,10,2)
[1, 3, 5, 7, 9]
>>> range(1,10,3)
[1, 4, 7]
>>> help(range)
Help on built-in function range in module __builtin__:
range(...)
range(stop) -> list of integers
range(start, stop[, step]) -> list of integers
Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3]. The end point is omitted!
These are exactly the valid indices for a list of 4 elements.
(END)
I am using python 3.6.This program should store the value of indexes of the numbers of one array into another (index starting with 1) For Eg if array is [2,3,1] the next one should be [3,1,2].. but while implemending the list gets changed.
I tried to do with respect to values of 1st array but no use it gets changed when doing the logic.
n = int(input())
arr = input()
l = list(map(int,arr.split(' ')))
arr1= l
print(l)
for i in range(0,n):
print(l[i])
arr1[l[i]-1]=i+1
print(arr1)
Answer should be [4,1,2,3] but answer is [2,1,4,3]
enter code here
You are asking for an array of the indexes of the numbers, but your example shows the indexes +1. It would probably be cleaner to stick with zero indexing.
Nevertheless, if you're looking for a (somewhat) flexible approach, you could do something like this with a list comprehension:
>>> l = [2, 3, 1]
>>> [l.index(i+1) +1 if i+1 in l else None for i in range(max(l))]
[3, 1, 2]
Given an array that's spread out, it will fill with None:
>>> l = [2, 3, 7, 1]
>>> [l.index(i+1) +1 if i+1 in l else None for i in range(max(l))]
[4, 1, 2, None, None, None, 3]
It will silently ignore dupes:
>>> l = [2, 2, 1, 2]
>>> [l.index(i+1) +1 if i+1 in l else None for i in range(max(l))]
[3, 1]
you can do it this way:
x = [2, 3, 1]
ind = [x.index(e) for e in sorted(x)]
# if you want your indices to start from 1
[e+1 for e in ind]
Why is this not working? Actual result is [] for any entry.
def non_unique(ints):
"""
Return a list consisting of only the non-unique elements from the list lst.
You are given a non-empty list of integers (ints). You should return a
list consisting of only the non-unique elements in this list. To do so
you will need to remove all unique elements (elements which are
contained in a given list only once). When solving this task, do not
change the order of the list.
>>> non_unique([1, 2, 3, 1, 3])
[1, 3, 1, 3]
>>> non_unique([1, 2, 3, 4, 5])
[]
>>> non_unique([5, 5, 5, 5, 5])
[5, 5, 5, 5, 5]
>>> non_unique([10, 9, 10, 10, 9, 8])
[10, 9, 10, 10, 9]
"""
new_list = []
for x in ints:
for a in ints:
if ints.index(x) != ints.index(a):
if x == a:
new_list.append(a)
return new_list
Working code (not from me):
result = []
for c in ints:
if ints.count(c) > 1:
result.append(c)
return result
list.index will return the first index that contains the input parameter, so if x==a is true, then ints.index(x) will always equal ints.index(a). If you want to keep your same code structure, I'd recommend keeping track of the indicies within the loop using enumerate as in:
for x_ind, x in enumerate(ints):
for a_ind, a in enumerate(ints):
if x_ind != a_ind:
if x == a:
new_list.append(a)
Although, for what it's worth, I think your example of working code is a better way of accomplishing the same task.
Although the example of working code is correct, if suffers from quadratic complexity which makes it slow for larger lists. I'd prefer s.th. like this:
from nltk.probability import FreqDist
def non_unique(ints):
fd = FreqDist(ints)
return [x for x in ints if fd[x] > 1]
It precomputes a frequency distribution in the first step, and then selects all non-unique elements. Both steps have a O(n) performance characteristic.
I've an array my_array and I want, due to specific reasons ignore the values -5 and -10 of it (yes, in the example below there's not a -10 but in other arrays I've to manage yes), and get the index of the three minimum values of the array, and append them to a new list titled lista_indices_candidatos.
This is my code.
my_array = [4, -5, 10, 4, 4, 4, 0, 4, 4]
a = np.array(my_array)
indices = a.argsort()
indices = indices[a[indices] != -5]
indices = indices[a[indices] != -10]
lista_indices_candidatos = []
for i in indices[:3]:
lista_indices_candidatos.append(i)
print lista_indices_candidatos
This gets me the index of the 3 minimum values [6, 0, 3] from the array [4, -5, 10, 4, 4, 4, 0, 4, 4]
The thing is that, if there are repeated values, this get's me the first three minimum values (the first 4 (index 0) the second 4 (index 3), ignoring the rest 4's of the array.
How can I change the code to get completely randomly the three minimum values, without taking always the first three?
myArray = [4, -5, 10, 4, 4, 4, 0, 4, 4]
myUniqueArray = list(set(myArray))
myUniqueArray.sort()
return [myArray.index(myUniqueArray[0]), myArray.index(myUniqueArray[1]), myArray.index(myUniqueArray[2])]
.index would not give you a random index in the sense that it will always be the same value for a give set of input list but you could play with that part.
I haven't introduced randomness, because it don't really see the point for doing this.
If you need the first 3 lowest positive values:
sorted([x for x in my_array if x >= 0])[:3]
If you need the first three lowest positive values and their initial index:
sorted([(x,idx) for idx,x in enumerate(my_array) if x >= 0], key=lambda t: t[0])[:3]
If you just need the first 3 lowest positive values initial indexes:
[i for x,i in sorted([(x,idx) for idx,x in enumerate(my_array) if x >= 0], key=lambda t: t[0])[:3]]
My take is that you want to get 3 random indices for values in my_array, excluding [-10, -5], the 3 random indices must be chosen within the index list of the 3 lowest values of the remaining set, right?
What about this:
from random import sample
my_array = [4, -5, 10, 4, 4, 4, 0, 4, 4]
sample([i for i, x in enumerate(my_array) if x in sorted(set(my_array) - {-10, -5})[:3]], 3)
Factoring out the limited set of values, that would be:
from random import sample
my_array = [4, -5, 10, 4, 4, 4, 0, 4, 4]
filtered_list = sorted(set(my_array) - {-10, -5})[:3]
# Print 3 sample indices from my_array
print sample([i for i, x in enumerate(my_array) if x in filtered_list], 3)
Ok, I'm also not sure what you are trying to achieve. I like the simplicity of Nasha's answer, but I think you want to always have the index of the 0 in the result set. The way I understand you, you want the index of the lowest three values and only if one of those values is listed more than once, do you want to pick randomly from those.
Here's my try a solution:
import random
my_array = [4, -5, 10, 4, 4, 4, 0, 4, 4]
my_dict = {}
lista_indices_candidatos = []
for index, item in enumerate(my_array):
try:
my_dict[item] = my_dict[item] + [index]
except:
my_dict[item] = [index]
for i in [x for x in sorted(my_array) if x != -10 and x != -5][:3]:
lista_indices_candidatos.append(random.choice(my_dict[i]))
print lista_indices_candidatos
In this solution, I build a dictionary with all the values from my_array as keys. The values of the dictionary is a list of indexes these numbers have in my_array. I then use a list comprehension and slicing to get the three lowest values to iterate over in the for loop. There, I can randomly pick an index for a given value by randomly selecting from my_dict.
I bet there are better ways to achieve what you want to achieve, though. Maybe you can let us know what it is you are trying to do so we can improve on our answers.
After reading your comment, I see that you do not actually want a completely random selection, but instead a random selection without repetition. So here's an updated version.
import random
my_array = [4, -5, 10, 4, 4, 4, 0, 4, 4]
my_dict = {}
lista_indices_candidatos = []
for index, item in enumerate(my_array):
try:
my_dict[item] = my_dict[item] + [index]
except:
my_dict[item] = [index]
for l in my_dict:
random.shuffle(my_dict[l])
for i in [x for x in sorted(my_array) if x != -10 and x != -5][:3]:
lista_indices_candidatos.append(my_dict[i].pop())
print lista_indices_candidatos
How about this one:
import random
def eachIndexSorted(a): # ... without -5 and -10
for value in sorted(set(a) - { -5, -10 }):
indexes = [ i for i in range(len(a)) if a[i] == value ]
random.shuffle(indexes)
for i in indexes:
yield i
def firstN(iterator, n):
for i in range(n):
yield iterator.next()
print list(firstN(eachIndexSorted(my_array), 3))
If you have very large data, then sorting the complete set might be too costly; finding each next minimum iteratively might then be a better approach. (Ask for more details if this aspect is unclear and important for you.)