Repeating same code block for creating different values - python

I'm making a program that basically calculates the missing values (x in this example) in multiple lists.
These are the lists:
L11=[1,3,5,'x',8,10]
L12=['x',3,3,'x',6,0]
L21=[6,1,1,9,2,2]
L22=[1,1,1,'x','x','x']
For example, I'm using this code block to find the x values in L22:
#How to find x:
#1--> a= calculate the sum of integers in the list
#2--> b=calculate the average of them
#3--> all values of x inside the list equal b
a22=L22.count('x')
for i in range(len(L22)):
if L22[i]=='x':
x_L22=round((sum([int(k) for k in L22 if type(k)==int]))/(len(L22)-a22))
So we find x_L22=1 and the new L22 is:
x_L22=1
L22=[1,1,1,1,1,1]
Now here is my question, I want to repeat this steps for all other lists without writing the same code. Is this possible?

Other answers focus on extracting your current code to a generic function which is useful but isn't neither sufficient nor necessary to apply the same piece of code on multiple input.
The only thing you need is to loop over your pieces of data :
L11=[1,3,5,'x',8,10]
L12=['x',3,3,'x',6,0]
L21=[6,1,1,9,2,2]
L22=[1,1,1,'x','x','x']
inputs = ( L11, L12, L21, L22 )
for input in inputs :
# your 4 previous lines on code, modified to work
# on the generic "input" instead of the specific "L22"
a=input.count('x')
for i in range(len(input)):
if input[i]=='x':
x=round((sum([int(k) for k in input if type(k)==int]))/(len(input)-a))
# or if you've extracted the above code to a function,
# just call it instead of using the above 4 lines of code.

try putting it in a function like this:
def list_foo(list_):
counted=list_.count('x')
for i in range(len(list_)):
if list_[i]=='x':
total=round((sum([int(k) for k in list_ if type(k)==int])) \
/(len(list_)-counted))
return total
use it in your main loop
x_L22 = list_foo(L22)
or x_L11 = list_foo(L11)

This is an excellent use case for functions in Python
def get_filled_list(list_of_numbers):
#How to find x:
#1--> a= calculate the sum of integers in the list
#2--> b=calculate the average of them
#3--> all values of x inside the list equal b
new_list=list_of_numbers.count('x')
for i in range(len(list_of_numbers)):
if list_of_numbers[i]=='x':
list_of_numbers = round(
(sum([int(k)
for k in list_of_numbers if type(k)==int]))/
(len(list_of_numbers)-new_list)
)
A11 = get_filled_list(L11)
# ,..

I'd write a function that receives a list as an input and returns the same list with the 'x' value replaced with a new value:
def calculateList(l):
nrX=l.count('x')
newList = []
for elem in l:
if elem == 'x':
x = int(round((sum([int(k) for k in l if type(k)==int]))/(len(l)-nrX)))
newList.append(x)
else:
newList.append(elem)
return newList
You can then call this function on all the list you have:
newL = calculateList(L22)
print(newL)
Output is:
[1, 1, 1, 1, 1, 1]
Or if you prefer you can create a list containing all the lists you want to evaluate:
allLists = [L11, L12, L21, L22]
And then you iterate over this list:
for l in allLists:
newL = calculateList(l)
print(newL)
Output is:
[1, 3, 5, 5, 8, 10]
[3, 3, 3, 3, 6, 0]
[6, 1, 1, 9, 2, 2]
[1, 1, 1, 1, 1, 1]

Related

I have a problem on python list comprehension code [duplicate]

Is it possible to define a recursive list comprehension in Python?
Possibly a simplistic example, but something along the lines of:
nums = [1, 1, 2, 2, 3, 3, 4, 4]
willThisWork = [x for x in nums if x not in self] # self being the current comprehension
Is anything like this possible?
No, there's no (documented, solid, stable, ...;-) way to refer to "the current comprehension". You could just use a loop:
res = []
for x in nums:
if x not in res:
res.append(x)
of course this is very costly (O(N squared)), so you can optimize it with an auxiliary set (I'm assuming that keeping the order of items in res congruent to that of the items in nums, otherwise set(nums) would do you;-)...:
res = []
aux = set()
for x in nums:
if x not in aux:
res.append(x)
aux.add(x)
this is enormously faster for very long lists (O(N) instead of N squared).
Edit: in Python 2.5 or 2.6, vars()['_[1]'] might actually work in the role you want for self (for a non-nested listcomp)... which is why I qualified my statement by clarifying there's no documented, solid, stable way to access "the list being built up" -- that peculiar, undocumented "name" '_[1]' (deliberately chosen not to be a valid identifier;-) is the apex of "implementation artifacts" and any code relying on it deserves to be put out of its misery;-).
Starting Python 3.8, and the introduction of assignment expressions (PEP 572) (:= operator), which gives the possibility to name the result of an expression, we could reference items already seen by updating a variable within the list comprehension:
# items = [1, 1, 2, 2, 3, 3, 4, 4]
acc = []; [acc := acc + [x] for x in items if x not in acc]
# acc = [1, 2, 3, 4]
This:
Initializes a list acc which symbolizes the running list of elements already seen
For each item, this checks if it's already part of the acc list; and if not:
appends the item to acc (acc := acc + [x]) via an assignment expression
and at the same time uses the new value of acc as the mapped value for this item
Actually you can! This example with an explanation hopefully will illustrate how.
define recursive example to get a number only when it is 5 or more and if it isn't, increment it and call the 'check' function again. Repeat this process until it reaches 5 at which point return 5.
print [ (lambda f,v: v >= 5 and v or f(f,v+1))(lambda g,i: i >= 5 and i or g(g,i+1),i) for i in [1,2,3,4,5,6] ]
result:
[5, 5, 5, 5, 5, 6]
>>>
essentially the two anonymous functions interact in this way:
let f(g,x) = {
expression, terminal condition
g(g,x), non-terminal condition
}
let g(f,x) = {
expression, terminal condition
f(f,x), non-terminal condition
}
make g,f the 'same' function except that in one or both add a clause where the parameter is modified so as to cause the terminal condition to be reached and then go
f(g,x) in this way g becomes a copy of f making it like:
f(g,x) = {
expression, terminal condition
{
expression, terminal condition,
g(g,x), non-terminal codition
}, non-terminal condition
}
You need to do this because you can't access the the anonymous function itself upon being executed.
i.e
(lambda f,v: somehow call the function again inside itself )(_,_)
so in this example let A = the first function and B the second. We call A passing B as f and i as v. Now as B is essentially a copy of A and it's a parameter that has been passed you can now call B which is like calling A.
This generates the factorials in a list
print [ (lambda f,v: v == 0 and 1 or v*f(f,v-1))(lambda g,i: i == 0 and 1 or i*g(g,i-1),i) for i in [1,2,3,5,6,7] ]
[1, 2, 6, 120, 720, 5040]
>>>
Not sure if this is what you want, but you can write nested list comprehensions:
xs = [[i for i in range(1,10) if i % j == 0] for j in range(2,5)]
assert xs == [[2, 4, 6, 8], [3, 6, 9], [4, 8]]
From your code example, you seem to want to simply eliminate duplicates, which you can do with sets:
xs = sorted(set([1, 1, 2, 2, 3, 3, 4, 4]))
assert xs == [1, 2, 3, 4]
no. it won't work, there is no self to refer to while list comprehension is being executed.
And the main reason of course is that list comprehensions where not designed for this use.
No.
But it looks like you are trying to make a list of the unique elements in nums.
You could use a set:
unique_items = set(nums)
Note that items in nums need to be hashable.
You can also do the following. Which is a close as I can get to your original idea. But this is not as efficient as creating a set.
unique_items = []
for i in nums:
if i not in unique_items:
unique_items.append(i)
Do this:
nums = [1, 1, 2, 2, 3, 3, 4, 4]
set_of_nums = set(nums)
unique_num_list = list(set_of_nums)
or even this:
unique_num_list = sorted(set_of_nums)

I am trying to make a function that takes in a list and gives out an edited list in python

I'm trying to make a function, called delta in this case, that will take in an in_list and return and out_list. These are the requirements for the out_list:
len(out_list) == len(in_list)-1
out_list(i) = in_list(i+1)-in_list(i)
I wish to use the function on the lists "times" and "positions" in the code below:
positionfile = open.("positionmeasurements.txt", "r", encoding="utf-8-sig")
filetext = positionfile.read().splitlines()
times = []
positions = []
for i in filetext:
time, position = i.split(";")
times.append(time)
positions.append(position)
This is the code I've got so so far for the function:
def delta(in_list):
for i in in_list:
out_list = in_list[i+1] - in_list[i]
return out_list
The following numbers are the times (left side of the semicolon) and the positions:
0.05;0.9893835
0.1;0.9921275
0.15;0.989212
0.2;0.98784
0.25;0.9876685
0.3;0.988526
0.35;0.991613
0.4;0.9921275
0.45;0.9921275
0.5;0.9886975
0.55;0.985096
0.6;0.983724
0.65;0.9578275
0.7;0.9163245
0.75;0.8590435
0.8;0.7890715
0.85;0.714812
0.9;0.642096
0.95;0.559776
1;0.4776275
1.05;0.398566
1.1;0.315903
1.15;0.2320395
1.2;0.1799035
1.25;0.181104
You can try the list comprehension. As the elements of your list are string, you must convert them into float :
def delta(in_list):
return [float(in_list[i+1]) - float(in_list[i]) for i in range(len(in_list) - 1)]
Part of me thinks it'd be more elegant to do this recursively, but the way you've done it is probably better if there's a very long list. Anyway, you're nearly there, you've just mixed up Python's for-loop syntax. for item in list gives you the items, for i in range(len(list)) gives indices to use with the list, which is what you want here.
def delta(in_list):
out_list = []
for i in range(len(in_list)-1):
out_list.append(in_list[i+1] - in_list[i])
return out_list
Just a list comprehension...
>>> in_list = [1, 1, 2, 3, 5, 8, 13, 21]
>>> [b - a for a, b in zip(in_list, in_list[1:])]
[0, 1, 1, 2, 3, 5, 8]

Assign values to array during loop - Python

I am currently learning Python (I have a strong background in Matlab). I would like to write a loop in Python, where the size of the array increases with every iteration (i.e., I can assign a newly calculated value to a different index of a variable). For the sake of this question, I am using a very simple loop to generate the vector t = [1 2 3 4 5]. In Matlab, programming my desired loop would look something like this:
t = [];
for i = 1:5
t(i,1) = i;
end
I have managed to achieve the same thing in Python with the following code:
result_t = []
for i in range(1,5):
t = i
result_t.append(t)
Is there a more efficient way to assign values to an array as we iterate in Python? Why is it not possible to do t[i,1] = i (error: list indices must be integers or slices, not tuple) or t.append(t) = t (error: 'int' object has no attribute 'append')?
Finally, I have used the example above for simplicity. I am aware that if I wanted to generate the vector [1 2 3 4 5] in Python, that I could use the function "np.arange(1,5,1)"
Thanks in advance for your assistance!
-> My real intention isn't to produce the vector [1 2 3 4 5], but rather to assign calculated values to the index of the vector variable. For example:
result_b = []
b = 2
for i in range(1,5):
t = i + b*t
result_b.append(t)
Why can I not directly write t.append(t) or use indexing (i.e., t[i] = i + b*t)?
Appending elements while looping using append() is correct and it's a built-in method within Python lists.
However you can have the same result:
Using list comprehension:
result_t = [k for k in range(1,6)]
print(result_t)
>>> [1, 2, 3, 4, 5]
Using + operator:
result_t = []
for k in range(1,6):
result_t += [k]
print(result_t)
>>> [1, 2, 3, 4, 5]
Using special method __iadd__:
result_t = []
for k in range(1,6):
result_t.__iadd__([k])
print(result_t)
>>> [1, 2, 3, 4, 5]
The range function returns an iterator in modern Python. The list function converts an iterator to a list. So the following will fill your list with the values 1 to 5:
result_t = list(range(1,6)) # yields [1, 2, 3, 4, 5]
Note that in order to include 5 in the list, the range argument has to be 6.
Your last example doesn't parse unless you assign t a value before the loop. Assuming you do that, what you're doing in that case is modifying t each time through the loop, not just producing a linear range. You can get this effect using the map function:
t = 0
b = 2
def f(i):
global t
t = i + b*t
return t
result_b = list(map(f, range(1, 5))) # Yields [1, 4, 11, 26]
The map function applies the f function to each element of the range and returns an iterator, which is converted into a list using the list function. Of course, this version is more verbose than the loop, for this small example, but the technique itself is useful.
a better example from UI Testing using selenium.
print('Assert Pagination buttons displayed?')
all_spans = self.web_driver.find_elements_by_tag_name('span')
# Identify the button texts
pagination_buttons = ["Previous Page", "Next Page", "First Page"]
# Filter from all spans, only the required ones.
filtered_spans = [s for s in all_spans if s.text in pagination_buttons]
# From the filtered spans, assert all for is_displayed()
for a_span in filtered_spans:
assert a_span.is_displayed()
print('Asserted Pagination buttons displayed.')
You can try this
data = ['Order-'+str(i) for i in range(1,6)]
print(data)
>>> ['Order-1', 'Order-2', 'Order-3', 'Order-4', 'Order-5']

Is it safe practice to edit a list while looping through it?

I was trying to execute a for loop like:
a = [1,2,3,4,5,6,7]
for i in range(0, len(a), 1):
if a[i] == 4:
a.remove(a[i])
I end up having an index error since the length of the list becomes shorter but the iterator i does not become aware.
So, my question is, how can something like that be coded? Can the range of i be updated in each iterations of the loop based on current array condition?
For the .pop() that you mention for example you can use a list comprehension to create a second list or even modify the original one in place. Like so:
alist = [1, 2, 3, 4, 1, 2, 3, 5, 5, 4, 2]
alist = [x for x in alist if x != 4]
print(alist)
#[1, 2, 3, 1, 2, 3, 5, 5, 2]
As user2393256 more generally puts it, you can generalize and define a function my_filter() which will return a boolean based on some check you implement in it. And then you can do:
def my_filter(a_value):
return True if a_value != 4 else False
alist = [x for x in alist if my_filter(x)]
I would go with the function solution if the check was too complicated to type in the list comprehension, so mainly for readability. The example above is therefore not the best since the check is very simple but i just wanted to show you how it would be done.
If you want to delete elements from your list while iterating over it you should use list comprehension.
a = [1,2,3,4,5,6,7]
a = [x for x in a if not check(x)]
You would need to write a "check" function that returns wether or not you want to keep the element in the list.
I don't know where you are going with that but this would do what I beleive you want :
i=0
a = [1,2,3,4,5,6,7]
while boolean_should_i_stop_the_loop :
if i>=len(a) :
boolean_should_i_stop_the_loop = False
#here goes what you want to do in the for loop
print i;
a.append(4)
i += 1

remove non repeating characters from a list

I am trying to remove non repeating characters from a list in python. e.g list = [1,1,2,3,3,3,5,6] should return [1,1,3,3].
My initial attempt was:
def tester(data):
for x in data:
if data.count(x) == 1:
data.remove(x)
return data
This will work for some inputs, but for [1,2,3,4,5], for example, it returns [2,4]. Could someone please explain why this occurs?
l=[1,1,2,3,3,3,5,6]
[x for x in l if l.count(x) > 1]
[1, 1, 3, 3, 3]
Adds elements that appear at least twice in your list.
In your own code you need to change the line for x in data to for x in data[:]:
Using data[:] you are iterating over a copy of original list.
There is a linear time solution for that:
def tester(data):
cnt = {}
for e in data:
cnt[e] = cnt.get(e, 0) + 1
return [x for x in data if cnt[x] > 1]
This is occurring because you are removing from a list as you're iterating through it. Instead, consider appending to a new list.
You could also use collections.Counter, if you're using 2.7 or greater:
[a for a, b in collections.Counter(your_list).items() if b > 1]
Another linear solution.
>>> data = [1, 1, 2, 3, 3, 3, 5, 6]
>>> D = dict.fromkeys(data, 0)
>>> for item in data:
... D[item] += 1
...
>>> [item for item in data if D[item] > 1]
[1, 1, 3, 3, 3]
You shouldn't remove items from a mutable list while iterating over that same list. The interpreter doesn't have any way to keep track of where it is in the list while you're doing this.
See this question for another example of the same problem, with many suggested alternative approaches.
you can use the list comprehention,just like this:
def tester(data):
return [x for x in data if data.count(x) != 1]
it is not recommended to remove item when iterating

Categories