python 3: lists dont change their values - python

So I am trying to change a bunch of list items by a random percentage using a for loop.
import random as rdm
list = [1000, 100, 50, 25]
def change():
for item in list:
item = item + item*rdm.uniform(-10, 10)
change()
print(list)
(also I dont get how to paste multiple lines of code so I did them one by one, would appreciate help with that too)
And now when it prints the list it only consists of the numbers it started with.

Your item =.... line, as it stands, just associates a new object with the name item in the function's namespace. There is no reason why this operation would change the content of the list object from which the previous value of item was extracted.
Here is a listing that changes a list in-place:
import random
lyst = [1000,100,50,25]
def change(lyst):
for i, item in enumerate(lyst):
item = item + item * random.uniform(-10, 10)
lyst[i] = item
print(lyst)
change(lyst)
print(lyst)
The lyst[i] =... assignment is the key line that actually changes the list's content. Of course you can collapse the two assignments into one line if you want: lyst[i] = item =..... Or you can omit the reassignment to item if you're not going to use it again in the loop: lyst[i] = item + item *...
Note that I performed two minor fixes in addition: I changed the variable name from list to lyst so that it doesn't overshadow your builtin reference to the list class. I have also altered your function so that it takes the list object as an argument, rather than relying on referring to it using a hard-coded global variable name. Both of these are just good practice; nothing to do with your problem.
Finally, note that you can do all of this much more succinctly and transparently with a so-called list comprehension. If you don't have to modify the list in-place, i.e. if it's OK to end up with a modified copy of the original list:
lyst = [ item + item * random.uniform(-10, 10) for item in lyst ]
If you need to modify the list in-place (e.g. if other references to the original list exist elsewhere and they, too, should point to the updated content after change() is called) then you can follow the suggestion in Padraic's comment:
lyst[:] = [ item + item * random.uniform(-10, 10) for item in lyst ]
In that last case, you can even save memory (which will only be a concern for very large lists) if you change the bracket shape and thereby use a generator expression instead:
lyst[:] = ( item + item * random.uniform(-10, 10) for item in lyst )

You need to store the computed value back to the list.
So replace:
for item in list:
item = item + item*rdm.uniform(-10, 10)
With:
for index, item in enumerate(list):
list[index] = item + item*rdm.uniform(-10, 10);
With the modified loop, you will be looping through each element in the list with its index. You can now assign back the computed value to that particular index in the list.

using functional approach and list comprehensions instead of for loop, you can create a new list with the changed items (unless you NEED to modify the existing list in place):
import random as rdm
items = [1000, 100, 50, 25]
def change(items, multiply_min=-10, multiply_max=10):
return [i * rdm.uniform(multiply_min, multiply_max) for i in items]
items = change(items)
print(items)

Related

What is the differences between del list[i] and list =list[:i] + list[i+1:]?

So the question is to delete an element at position i in a list, what are the differences between the following actions?
i = 3
l = [0,1,2,3,4,5]
del l[i]
i = 3
l = [0,1,2,3,4,5]
l = l[:i] + l[i+1:]
Both ways give the list the same result. But what are the essential differences?
Why is the second operation not acceptable?
But what are the essential differences?
The del way modifies the list in place. This means that any other names for that list will have the change reflected:
i = 3
l = [0,1,2,3,4,5]
k = l
del l[i]
# k has changed
The id() of the list does not change, because it is still the same list - only its contents have changed (just like if you used e.g. .append or .pop, or assigned to an element).
The slicing way creates a new list. This means that other names for the list will not have the change reflected - because they are still names for the original list, and that list was never changed.
i = 3
l = [0,1,2,3,4,5]
k = l
l = l[:i] + l[i+1:]
# k has not changed
If there are no other names for the original list, then the slicing way allows for the original list to be garbage-collected. The new list will probably not have the same id() as the original, because the old list's contents have to stick around during construction of the new one. There will be a temporary spike in the amount of memory used, which may matter for some programs with millions of elements in the list (or with elements that are very large objects).
If there are other names for the original list, then the new list will have a different id(), because it is a different list and the original still exists.
So the question is to delete an element at position i in a list
not
"to create another list without element i". That's why the second operation is not acceptable

Quicker way to filter lists based on a check to external variable?

I have a variable = 'P13804'
I also have a list like this:
['1T9G\tA\t2.9\tP11310\t241279.81', '1T9G\tS\t2.9\tP38117\t241279.81', '1T9G\tD\t2.9\tP11310\t241279.81', '1T9G\tB\t2.9\tP11310\t241279.81', '1T9G\tR\t2.9\tP13804\t241279.81', '1T9G\tC\t2.9\tP11310\t241279.81']
You can see, if you split each item in this list up by tab, that the third item in each sub-list of this list is sometimes 'P11310' and sometimes is 'P13804'.
I want to remove the items from the list, where the third item does not match my variable of interest (i.e. in this case P13804).
I know a way to do this is:
var = 'P13804'
new_list = []
for each_item in list1:
split_each_item = each_item.split('\t')
if split_each_item[3] != var:
new_list.append(each_item)
print(new_list)
In reality, the lists are really long, and i have a lot of variables to check. So I'm wondering does someone have a faster way of doing this?
It is generally more efficient in Python to build a list with a comprehension than repeatedly appending to it. So I would use:
var = 'P13804'
new_list = [i for i in list1 if i.split('\t')[2] == var]
According to timeit, it saves more or less 20% of the elapsed time.

How to create a new list, by appending a value in a certain position on and old list

I am trying to make a new list, which is a replica of an other list. This new list I want to modify by changing values at specific positions.
I found this question, and the most upvoted answer can make a new list and add on to the old list. This is a good start, but I want to decide where the additions are done (in his version, it just adds it on to the end of the list)
What he has:
myList = [10,20,30]
yourList = myList + [40]
print("your list: " + yourList)
your list: [10,20,30,40]
In this instance, I would want to be able to change where the 40 in your list goes ie
your list: [10,40,20,30]
I'm unsure how I would go about doing that. Thanks! (Pretty new here sorry if I'm not clear)
Use slicing to split the original list apart at the desired index, then put it back together again.
For example:
insert_at = 1
myList = [10,20,30]
# Uses PEP 448 unpacking generalizations to avoid excess temporary `list`s
yourList = [*myList[:insert_at], 40, *my_list[insert_at:]]
# Or via concatenation (involves an extra temporary)
yourList = myList[:insert_at] + [40] + my_list[insert_at:]
print("your list: " + yourList)
Alternatively, if you don't need a one-liner, copy the list, then call insert on the copy:
yourList = myList[:] # myList.copy() also works on Python 3
yourList.insert(insert_at, 40)
To keep the same list:
Use the insert method of the list class
myList.insert(1, 40)
insert(index, element) inserts the element into the list at the position index - shifting the remaining elements rightward as required.
To make a new list:
new_list = myList.copy()
new_list.insert(1, 40)
you can copy your list and insert the value in the new list something like this:
my_list = [1,2,3,4,5,6]
my_2_list = my_list.copy()
then i wnat to insert a vlue at position 3 i can do
my_2_list.insert(3, value)

Python: trying to get three elements of a list with slice over a iterator

I'm new to python.
I'm trying to create another list from a big one just with 3 elements of that list at a time.
I'm trying this:
my_list = ['test1,test2,test3','test4,test5,test6','test7,test8,test9','test10,test11,test12']
new_three = []
for i in my_list:
item = my_list[int(i):3]
new_three.append(item)
# here I'll write a file with these 3 elements. Next iteration I will write the next three ones, and so on...
I'm getting this error:
item = my_list[int(i):3]
ValueError: invalid literal for int() with base 10: 'test1,test2,test3'
I also tried:
from itertools import islice
for i in my_list:
new_three.append(islice(my_list,int(i),3))
Got the same error. I cannot figure out what I'm doing wrong.
EDIT:
After many tries with help here, I could make it.
listrange = []
for i in range(len(li)/3 + 1):
item = li[i*3:(i*3)+3]
listrange.append(item)
Is this what you meant?
my_list = ['test1,test2,test3','test4,test5,test6','test7,test8,test9','test10,test11,test12']
for item in my_list:
print "this is one item from the list :", item
list_of_things = item.split(',')
print "make a list with split on comma:", list_of_things
# you can write list_of_things to disk here
print "--------------------------------"
In response to comments, if you want to generate a whole new list with the comma separated strings transformed into sublists, that is a list comprehension:
new_list = [item.split(',') for item in my_list]
And to split it up into groups of three items from the original list, see the answer linked in comments by PM 2Ring, What is the most "pythonic" way to iterate over a list in chunks?
I have adapted that to your specific case here:
my_list = ['test1,test2,test3','test4,test5,test6','test7,test8,test9','test10,test11,test12']
for i in xrange(0, len(my_list), 3):
# get the next three items from my_list
my_list_segment = my_list[i:i+3]
# here is an example of making a new list with those three
new_list = [item.split(',') for item in my_list]
print "three items from original list, with string split into sublist"
print my_list_segment
print "-------------------------------------------------------------"
# here is a more practical use of the three items, if you are writing separate files for each three
filename_this_segment = 'temp' # make up a filename, possibly using i/3+1 in the name
with open(filename_this_segment, 'w') as f:
for item in my_list_segment:
list_of_things = item.split(',')
for thing in list_of_things:
# obviously you'll want to format the file somehow, but that's beyond the scope of this question
f.write(thing)

Are lists immutable in python? And are they passed value in a function?

def threshold(the_list,thresholding_value,upper_val,lower_value):
ret_list=list(the_list)
for each_item in ret_list:
if each_item <= thresholding_value:
each_item=lower_value
else:
each_item=upper_val
return ret_list
temp=[-1,2,5,3,-1,5,32,-6]
ttt=r.threshold(temp,0,1,0)
after exection I am still getting the value of list same
Lists are not immutable, but when you loop over one, the loop control variable gets a copy of each item; it's not an alias for it. So changing it doesn't affect the list.
A more Pythonic way to do this would be a list comprehension returning the new list:
def threshold(the_list, threshold, upper, lower):
return [lower if item <= threshold else upper for item in the_list]
threshold([-1,2,5,3,-1,5,32,-6],0,1,0)
# => [0, 1, 1, 1, 0, 1, 1, 0]
each_item is just a local variable which has its value overriden with the = operator. Assigning a value to it won't affect the original list. Instead, you could create a new list and fill it up before returning it:
def threshold(the_list, thresholding_value, upper_val, lower_val):
ret_list = list()
for each_item in the_list:
if each_item <= thresholding_value:
ret_list.append(lower_val)
else:
ret_list.append(upper_val)
return ret_list
You could also shorten this code significantly by using list comprehensions:
def threshold(the_list, thresholding_value, upper_val, lower_val):
return [lower_val if x <= thresholding_value else upper_val for x in the_list]
Lists are most definitely mutable. What happens is you iterate over the list, with each element copied to each_item in turn. What happens to each_item is not relevant to the list, as it is just a temp variable to hold your value, not a pointer.
Representing each_item as a 1-element list or a class would fix your issue, but the whole approach is not how it is usually done in python: why create a copy of list when you can create an empty list for return value and add elements as you go?

Categories

Resources