Related
GOAL: Filter a list of lists using dictionary as reference in Python 3.8+
CASE USE: When reviewing a nested list -- a series of survey responses -- filtering out responses based on control questions. In the dictionary, the responses to questions 3 (index 2 in list) and 7 (index 6) should both be of corresponding value 5. If both answers for a response are not 5, they should not be populated in the filtered_responses list.
Open to interpretation on how to solve for this. I have reviewed several resources touching on filtering dictionaries using lists. This method is preferred as some survey responses many contain the same array of values, therefore the list element is retained.
no_of_survey_questions = 10
no_of_participants = 5
min_score = 1
max_score = 10
control_questions = {3: 5,
7: 5, }
unfiltered_responses = [[4, 5, 4, 5, 4, 5, 4, 5, 4, 5], # omit
[9, 8, 7, 6, 5, 4, 3, 2, 1, 1], # omit
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5], # include
[5, 2, 5, 2, 5, 2, 5, 9, 1, 1], # include
[1, 2, 5, 1, 2, 1, 2, 1, 2, 1]] # omit
for response in unfiltered_responses:
print(response)
print()
filtered_responses = [] # should contain only unfiltered_responses values marked 'include'
for response in filtered_responses:
# INSERT CODE HERE
print(response)
Thanks in advance!
You can use list comprehension + all():
control_questions = {3: 5,
7: 5}
unfiltered_responses = [[4, 5, 4, 5, 4, 5, 4, 5, 4, 5], # omit
[9, 8, 7, 6, 5, 4, 3, 2, 1, 1], # omit
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5], # include
[5, 2, 5, 2, 5, 2, 5, 9, 1, 1], # include
[1, 2, 5, 1, 2, 1, 2, 1, 2, 1]] # omit
filted_questions = [subl for subl in unfiltered_responses if all(subl[k-1] == v for k, v in control_questions.items())]
print(filted_questions)
Prints:
[
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[5, 2, 5, 2, 5, 2, 5, 9, 1, 1]
]
Suppose that I have the following array or arrays:
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],[0, 1, 2, 3, 4, 5],[0, 1, 2, 3, 4, 5, 6, 7]]
What's the best way to loop into the main array and randomly extract one number from each sub-array every time and create another array with them? For instance, in the first pass, the result would be:
[2,5,6]
The second pass could be:
[8,0,7]
etc. At this stage I don't have any clues how to do it.
If you have python lists, you can use random.choice in a list comprehension:
L = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],[0, 1, 2, 3, 4, 5],[0, 1, 2, 3, 4, 5, 6, 7]]
from random import choice
out = [choice(l) for l in L]
example output:
[3, 0, 5]
variant
Imagine you want to pick each item only a single time in each iteration, you could also use pop on a random position:
L = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],[0, 1, 2, 3, 4, 5],[0, 1, 2, 3, 4, 5, 6, 7]]
for i in range(6):
print([l.pop(np.random.randint(len(l))) for l in L])
Obviously, here you cannot have a number of iterations greater than the length of the shortest sublist
example output:
[5, 4, 0]
[2, 2, 6]
[3, 5, 7]
[0, 1, 1]
[4, 3, 2]
[9, 0, 3]
I have an array like this:
a = [[ 8, 7, 6, 5, 9],
[1, 2, 1, 6, 4],
[4, 2, 5, 4, 2]]`
I want to change the order of that array based on second row with an order like this:
b = [2, 6, 1, 1, 4]
So, I want the result becomes like this:
a = [[7, 5, 8, 6, 9],
[2, 6, 1, 1, 4],
[2, 4, 4, 5, 2]]
How can I solve this problem in Python?
a = [[ 8, 7, 6, 5, 9],
[1, 2, 1, 6, 4],
[4, 2, 5, 4, 2]]
a[1] = [2, 6, 1, 1, 4]
Try that.
In this answer, I'm making the following two assumptions:
All sub-lists are 5 elements in length
The desired logic is to move the 2nd and 4th elements to be 1st and 2nd respectively
If both of the assumptions made above are true, you can use list comprehension on a nested list, and create a list to specify how the lists should be reordered.
a = [[8, 7, 6, 5, 9],
[1, 2, 1, 6, 4],
[4, 2, 5, 4, 2]]
new_ord = [1, 3, 0, 2, 4]
b = [[l[i] for i in new_ord] for l in a]
print(b) #prints: [[7, 5, 8, 6, 9], [2, 6, 1, 1, 4], [2, 4, 4, 5, 2]]
In Python, I cannot create a list in which every item is a different list.
This is an example:
a = [1,2,3,4,5,6,7,8,9]
b = []
c = []
for i in a:
b.append(i)
c.append(b)
c
the result is:
[[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9]]
instead, what I would reach is:
[[1],
[1, 2],
[1, 2, 3],
[1, 2, 3, 4],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5, 6],
[1, 2, 3, 4, 5, 6, 7],
[1, 2, 3, 4, 5, 6, 7, 8],
[1, 2, 3, 4, 5, 6, 7, 8, 9]]
May you please help me?
By doing c.append(b) you're putting the b instance, so b is everywhere in c, and as you fill b you see it in all boxes of c, you need to make a copy with on these ways
c.append(list(b))
c.append(b[:])
Regarding the task itself, I'd propose another way to do it:
for end in a:
c.append(list(range(1, end + 1)))
Which corresponds to c = [list(range(1, end + 1)) for end in a] in list comprehension
In Python, variables holds references to the Objects. When you append your list b to another list c, you basically copy the reference of b to your list c (NOT THE OBJECT'S CONTENT). Since, list are mutuable, when you modify your list b (after appending it to list c), it's updated value will also be reflected in c.
Try this code to learn more:
a = [10]
c = a
a.append(100)
print(c)
Outputs:
[10, 100]
You can either do:
c.append(b[:])
OR
c.append(list(b))
OR
You can also use deepcopy in Python.
import copy
a = [1,2,3,4,5,6,7,8,9]
b = []
c = []
for i in a:
b.append(i)
c.append(copy.deepcopy(b))
print(c)
I'm coding with python 3.6 and am working on a Genetic Algorithm. When generating a new population, when I append the new values to the array all the values in the array are changed to the new value. Is there something wrong with my functions?
Code:
from fuzzywuzzy import fuzz
import numpy as np
import random
import time
def mutate(parent):
x = random.randint(0,len(parent)-1)
parent[x] = random.randint(0,9)
print(parent)
return parent
def gen(cur_gen, pop_size, fittest):
if cur_gen == 1:
population = []
for _ in range(pop_size):
add_to = []
for _ in range(6):
add_to.append(random.randint(0,9))
population.append(add_to)
return population
else:
population = []
for _ in range(pop_size):
print('\n')
population.append(mutate(fittest))
print(population)
return population
def get_fittest(population):
fitness = []
for x in population:
fitness.append(fuzz.ratio(x, [9,9,9,9,9,9]))
fittest = fitness.index(max(fitness))
fittest_fitness = fitness[fittest]
fittest = population[fittest]
return fittest, fittest_fitness
done = False
generation = 1
population = gen(generation, 10, [0,0,0,0,0,0])
print(population)
while not done:
generation += 1
time.sleep(0.5)
print('Current Generation: ',generation)
print('Fittest: ',get_fittest(population))
if get_fittest(population)[1] == 100:
done = True
population = gen(generation, 10, get_fittest(population)[0])
print('Population: ',population)
Output:
Fittest: ([7, 4, 2, 7, 8, 9], 72)
[3, 4, 2, 7, 8, 9]
[[3, 4, 2, 7, 8, 9]]
[3, 4, 2, 7, 5, 9]
[[3, 4, 2, 7, 5, 9], [3, 4, 2, 7, 5, 9]]
[3, 4, 2, 7, 4, 9]
[[3, 4, 2, 7, 4, 9], [3, 4, 2, 7, 4, 9], [3, 4, 2, 7, 4, 9]]
[3, 1, 2, 7, 4, 9]
[[3, 1, 2, 7, 4, 9], [3, 1, 2, 7, 4, 9], [3, 1, 2, 7, 4, 9], [3, 1, 2, 7, 4, 9]]
[3, 1, 2, 7, 4, 2]
[[3, 1, 2, 7, 4, 2], [3, 1, 2, 7, 4, 2], [3, 1, 2, 7, 4, 2], [3, 1, 2, 7, 4, 2], [3, 1, 2, 7, 4, 2]]
[3, 1, 2, 5, 4, 2]
[[3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2]]
[3, 1, 2, 5, 4, 2]
[[3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2], [3, 1, 2, 5, 4, 2]]
[3, 1, 2, 5, 4, 5]
[[3, 1, 2, 5, 4, 5], [3, 1, 2, 5, 4, 5], [3, 1, 2, 5, 4, 5], [3, 1, 2, 5, 4, 5], [3, 1, 2, 5, 4, 5], [3, 1, 2, 5, 4, 5], [3, 1, 2, 5, 4, 5], [3, 1, 2, 5, 4, 5]]
[3, 1, 2, 5, 4, 3]
[[3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3], [3, 1, 2, 5, 4, 3]]
You keep passing fittest, a reference to the same list, to mutate, which modifies the list in-place, and append it to population, so changes to the list in the next iteration are reflected across all the references to fittest in the population list.
You should pass a copy of the fittest list to mutate instead:
population.append(mutate(fittest[:]))
It's right there in the name:
def mutate(parent):
x = random.randint(0,len(parent)-1)
parent[x] = random.randint(0,9)
print(parent)
return parent
mutate isn't making a new list, it's modifying the existing list in place and returning a reference to the same list it was passed. population.append(mutate(fittest)) is happily storing aliases to fittest over and over, and since fittest is never replaced, it just keeps getting mutated and new aliases to it stored.
If the goal is to store a snapshot of the list at a given stage each time, copy it, changing:
population.append(mutate(fittest))
to:
population.append(mutate(fittest)[:])
where a complete slice of the list makes a shallow copy. population.append(mutate(fittest).copy()) would also work on modern Python, as would import copy, then doing population.append(copy.copy(mutate(fittest))) or (if the contents might themselves be mutable, though they aren't in this case) population.append(copy.deepcopy(mutate(fittest))).
Note that as a rule, Python functions aren't really supposed to both mutate and return the mutated value, as it leads to confusion like this.
Pythonic code intended to mutate in place returns None (implicitly usually, by not returning at all), so the code you'd use would actually be:
def mutate(parent):
x = random.randint(0,len(parent)-1)
parent[x] = random.randint(0,9)
print(parent)
and used with:
mutate(fittest) # Mutate fittest in place
population.append(fittest[:]) # Append copy of most recently updated fittest