I am trying to use this code that produces all possible outcomes of a dice roll, given the number of dice and number of sides. This codes works (but I do not quite understand how the list comprehension is working.
def dice_rolls(dice, sides):
"""
Equivalent to list(itertools.product(range(1,7), repeat=n)) except
for returning a list of lists instead of a list of tuples.
"""
result = [[]]
print([range(1, sides + 1)] * dice)
for pool in [range(1, sides + 1)] * dice:
result = [x + [y] for x in result for y in pool]
return result
Therefore, I am trying to re-write the list comprehension
result = [x + [y] for x in result for y in pool]
into FOR loops to try to make sense of how it is working, but am currently unable to properly do it. Current failed code:
for x in result:
for y in pool:
result = [x + [y]]
2nd Question: If I wanted to make this into a generator (because this function is a memory hog if you have enough dice and sides), would I simply just yield each item in the list as it is being produced instead of throwing it into the result list?
EDIT: I came up with a way to break the list comprehension into loops after getting great responses and wanted to capture it:
def dice_rolls(dice, sides):
result = [[]]
for pool in [range(1, sides + 1)] * dice:
temp_result = []
for existing_values in result: # existing_value same as x in list comp.
for new_values in pool: # new_value same as y in list comp.
temp_result.append(existing_values + [new_values])
result = temp_result
return result
My first instinct for this problem (and list comprehensions in general) would be to use recursion. Though YOU have asked for LOOPS, which is surprisingly challenging.
This is what I came up with;
def dice_rollsj(dice, sides):
result = [[]]
for num_dice in range(dice):
temp_result = []
for possible_new_values in range(1, sides+1):
for existing_values in result:
new_tuple = existing_values + [possible_new_values]
temp_result.append(new_tuple)
result = temp_result
I think that you'll get the same correct answers, but the numbers will be differently ordered. This may be due to the way the values are appended to the list. I don't know.... Let me know if this helps.
I tried to add as many lines as I could, because the goal was to expand and understand the comprehension.
you are redefining result with every iteration of the for y in pool loop. I believe what you want is to append the results:
result = []
for x in result:
for y in pool:
result.append(x + [y])
Related
Quick question about multiplying elements inside a list:
I have a list with 5 lists inside a list, containing two float values each.
str_list = [[-8.0, 0.070879], [-67.0, 0.263145], [92.0, 0.034718], [19.0, 0.231517], [35.0, 0.399741]]
Now, I am trying to multiply each of these elements by themselves, and then print them into a list containing the 5 variables that have been multiplied:
num_list = [-0.567032, -17.630715, 3.194056, 4.398823, 13.990935]
Here is the code that I am using, but the output is not the one desired above.
num_list = []
for pair in str_list:
for x in pair:
product = 1
product = product * x
num_list.append(x)
The result I am getting is
[0.070879, 0.263145, 0.034718, 0.231517, 0.399741]
which means that the program is only taking the second half of my lists and not actually multiplying anything.
It seems like a very basic error that I cannot see. Thank you for the help.
One Liner in Python using list comprehension (If your inner list will always have only two integers):
str_list = [[-8.0, 0.070879], [-67.0, 0.263145], [92.0, 0.034718], [19.0, 0.231517], [35.0, 0.399741]]
print([i[0]*i[1] for i in str_list])
Outputs:
[-0.567032, -17.630715000000002, 3.194056, 4.398823, 13.990935]
One Liner using functools (handles any number of integers in your inner list):
from functools import reduce
num_list = [reduce(lambda x, y: x*y, i) for i in str_list]
print(num_list)
Your code has alot of issues. The issues are listed below:
variable product should be moved from inner for loop to outer for loop as it it being reinitialized to 1 for every element of a pair in your list.
You should append product not x in your num_list.
You should append the product in your outer for loop (not in your inner for loop)
The right way to do it:
num_list = []
for pair in str_list:
product = 1
for x in pair:
product *= x
num_list.append(product)
print(num_list)
You are mistaken for logic.
num_list = []
for pair in str_list:
product = 1
for x in pair:
product = product * x
num_list.append(x)
Thanks
I have a curious case, and after some time I have not come up with an adequate solution.
Say you have two lists and you need to find items that have the same index.
x = [1,4,5,7,8]
y = [1,3,8,7,9]
I am able to get a correct intersection of those which appear in both lists with the same index by using the following:
matches = [i for i, (a,b) in enumerate(zip(x,y)) if a==b)
This would return:
[0,3]
I am able to get a a simple intersection of both lists with the following (and in many other ways, this is just an example)
intersected = set(x) & set(y)
This would return this list:
[1,8,7,9]
Here's the question. I'm wondering for some ideas for a way of getting a list of items (as in the second list) which do not include those matches above but are not in the same position on the list.
In other words, I'm looking items in x that do not share the same index in the y
The desired result would be the index position of "8" in y, or [2]
Thanks in advance
You're so close: iterate through y; look for a value that is in x, but not at the same position:
offset = [i for i, a in enumerate(y) if a in x and a != x[i] ]
Result:
[2]
Including the suggested upgrade from pault, with respect to Martijn's comment ... the pre-processing reduces the complexity, in case of large lists:
>>> both = set(x) & set(y)
>>> offset = [i for i, a in enumerate(y) if a in both and a != x[i] ]
As PaulT pointed out, this is still quite readable at OP's posted level.
I'd create a dictionary of indices for the first list, then use that to test if the second value is a) in that dictionary, and b) the current index is not present:
def non_matching_indices(x, y):
x_indices = {}
for i, v in enumerate(x):
x_indices.setdefault(v, set()).add(i)
return [i for i, v in enumerate(y) if i not in x_indices.get(v, {i})]
The above takes O(len(x) + len(y)) time; a single full scan through the one list, then another full scan through the other, where each test to include i is done in constant time.
You really don't want to use a value in x containment test here, because that requires a scan (a loop) over x to see if that value is really in the list or not. That takes O(len(x)) time, and you do that for each value in y, which means that the fucntion takes O(len(x) * len(y)) time.
You can see the speed differences when you run a time trial with a larger list filled with random data:
>>> import random, timeit
>>> def using_in_x(x, y):
... return [i for i, a in enumerate(y) if a in x and a != x[i]]
...
>>> x = random.sample(range(10**6), 1000)
>>> y = random.sample(range(10**6), 1000)
>>> for f in (using_in_x, non_matching_indices):
... timer = timeit.Timer("f(x, y)", f"from __main__ import f, x, y")
... count, total = timer.autorange()
... print(f"{f.__name__:>20}: {total / count * 1000:6.3f}ms")
...
using_in_x: 10.468ms
non_matching_indices: 0.630ms
So with two lists of 1000 numbers each, if you use value in x testing, you easily take 15 times as much time to complete the task.
x = [1,4,5,7,8]
y = [1,3,8,7,9]
result=[]
for e in x:
if e in y and x.index(e) != y.index(e):
result.append((x.index(e),y.index(e),e))
print result #gives tuple with x_position,y_position,value
This version goes item by item through the first list and checks whether the item is also in the second list. If it is, it compares the indices for the found item in both lists and if they are different then it stores both indices and the item value as a tuple with three values in the result list.
I've been trying to convert some of my functions where I use a for loop by using list comprehension. Here is my first version of the function,
def adstocked_advertising(data, adstock_rate):
'''
Transforming data with applying Adstock transformations
data - > The dataframe that is being used to create Adstock variables
adstock_rate -> The rate at of the adstock
ex. data['Channel_Adstock'] = adstocked_advertising(data['Channel'], 0.5)
'''
adstocked_advertising = []
for i in range(len(data)):
if i == 0:
adstocked_advertising.append(data[i])
else:
adstocked_advertising.append(data[i] + adstock_rate * adstocked_advertising[i-1])
return adstocked_advertising
I want to convert it to this,
def adstocked_advertising_list(data, adstock_rate):
adstocked_advertising = [data[i] if i == 0 else data[i] + adstock_rate * data[i-1] for i in range(len(data))]
return adstocked_advertising
However, when viewing the df after running both functions I get two different values.
data['TV_adstock'] = adstocked_advertising_list(data['TV'], 0.5)
data['TV_adstock_2'] = adstocked_advertising(data['TV'], 0.5)
here is output,
data.head()
data.tail()
I am not too sure why the first two rows are the same and then from there the numbers are all different. I am new to list comprehension so I may be missing something here.
You need to refer to the previously generated element in the list, and list comprehensions are not well suited to this type of problem. They work well for operations that only need to look at a single element at once.
This question goes into more detail.
In your initial example, you use adstock_rate * adstocked_advertising[i-1]. The list comprehension version uses adstock_rate * data[i-1], which is why you are getting different results.
A standard for loop works just fine for your use case. You could switch to using enumerate, as for i in range(len(data)) is discouraged.
if data:
res = [data[0]]
for index, item in enumerate(data[1:]):
results.append(item + rate * data[index-1])
You've changed your logic in the list comp version. Originally, your else formula looked like:
data[i] + adstock_rate * adstocked_advertising[i-1]
But the list comprehension version looks like:
data[i] + adstock_rate * data[i-1]
The first version accesses the i-1th element of the result list, while the second version accesses the i-1th element of the input list.
index == 0 is only true once at the beginning of the list. Why not eliminate the conditional:
def adstocked_advertising(data, adstock_rate):
if data:
res = [data[0]]
for i in range(1, len(data)):
res.append(data[i] + adstock_rate * res[i-1])
return res
I have been trying to use lists so I can separate my integers into something organized (which lists are made to do, right?). Making these integers as random numbers are also makes this a little more confusing. This is what I have so far:
import random
first_list = []
for first_num in range(5):
first_list = (random.randint(1,9))
print ("First List",first_list)
for x in range(5):
num_two = random.randint(2,8)
print ("Second List",num_two)
My display is random numbers going down with my printed First List/Second List connected to every number.
First List:8
First List 3
etc.
Second List:3
Second List 7
etc.
Second part of my assignment is to use to compare elements in the two lists in pairs, i.e., compare the first elements in both lists, compare the second elements in the both lists which has to show the larger number in each comparison. I just wanted to show what the conclusion is as to why I need help from you all in the first place.
Your professor/teacher/whoever is testing your knowledge of the list.append() method and the zip() method.
list.append() adds an element to a list. zip() yields an object that allows for iterating through multiple iterables at the same time.
read the python docs.
https://docs.python.org/3/
import random
first_list = []
second_list = []
for x in range(5):
first_list.append(random.randint(1,9))
for y in range(5):
second_list.append(random.randint(1,9))
print('First List:\n')
for num in first_list:
print(str(num)+',\n')
print('Second List:\n')
for num in second_list:
print(str(num)+',\n')
both_lists = zip(first_list, second_list)
for first, second in both_lists:
if first > second:
print('First is greater: ' + str(first))
elif first < second:
print('Second is greater: ' + str(second))
else:
print('First and second are equal: ' + str(first) + ', ' + str(second))
Martijn's answer is much more pythonic and elegant, although for printing the results, max() would need to be extended with a custom function to pass to map(), but I'm assuming that you don't understand list comprehension. This is the beginner's answer.
Your question is confusing, May be you are looking for something like this?
import random
first_list = []
Second_list=[]
for first_num in range(5):
first_list.append(random.randint(1,9))
for x in range(5):
Second_list.append(random.randint(2,8))
print([max(i) for i in zip(first_list,Second_list)])
You can also try :
print(list(map(max,zip(first_list,Second_list))))
Shorter version of your code:
import random
first_list = [random.randint(1,9) for first_num in range(5)]
Second_list=[random.randint(2,8) for x in range(5)]
print(list(map(max,zip(first_list,Second_list))))
I have a function which looks like that:
def roulette(self):
sum = 0
lst = []
for x in self.drinkList:
sum += x.fitness
lst.append(sum)
return lst
Can it be replaced with list comprehension expression or something more efficient than for loop?
PS: it apperars that if I do random.randrange(0), it raises an exception ValueError: empty range for randrange(). Is there a way to avoid it without using if test?
It's actually possible to 'peek' at the list being built in a list comprehension. the outermost list has the name _[1], which of course is not a valid python identifier, so it must be accessed in another way:
def roulette(self):
return [drink.fitness + (locals()['_[1]'][-1] if locals()['_[1]'] else 0)
for drink
in self.drinkList]
But just because you can doesn't mean you should; go with your for loop, it looks like exactly what it does, and also doesn't rely on an undocumented python feature.
Your roulette function is computing the partial sums of the list
of x.fitness elements.
You can reach the same result by defining a closure and using map on
a generator expression.
sum = 0
def partial_sum(x):
sum += x
return sum
lst = map(partial_sum, (x.fitness for x in self.drinkList))
This is certainly less readable than a for loop; it could be faster
but you'll have to experiment: map is generally faster than for,
but function calls are slow. (Substituting a list comprehension for
the generator expression might speed things up at the expense of
memory.)
[sum(x.fitness for x in self.drinklist[:i+1]) for i in range(len(self.drinklist))]
But this would be O(n^2), while yours is O(n).
I don't know if it is more efficient, but this has a list comprehension and less lines:
def roulette(self):
lst = [self.drinkList.pop(0).fitness]
[ lst.append(x.fitness + lst[-1]) for x in self.drinkList]
return lst
.
Edit
As nothing authorizes me to modify the list self.drinkList , I rewrite:
def roulette(self):
lst = [0]
[ lst.append(x.fitness + lst[-1]) for x in self.drinkList]
return lst[1:]