Replace elements in lists based on indexes python - python

Community of stackoverflow:
I have a list "rest" which is the following:
rest=[5, 7, 11, 4]
I have another list which is b:
b=[21, 22, 33, 31, 23, 15, 19, 13, 6]
And I have a "last" list:
last=[33, 19, 40, 21, 31, 22, 6, 15, 13, 23]
I have to replace the first 4 elements in b with the elements in rest. How can I replace the elements in last according to the matches with b to get the rest elements?
for example:
5 7 11 4 #elements from rest
b= [21, 22, 33, 31, 23, 15, 19, 13, 6]
to get last list as the following:
last=[11, 19, 40, 5, 4, 7, 6, 15, 13, 23] #elements that matched with b were replaced by rest
How can I do this?

Try this:
rest=[5, 7, 11, 4]
b=[21, 22, 33, 31, 23, 15, 19, 13, 6]
last=[33, 19, 40, 21, 31, 22, 6, 15, 13, 23]
for i, l in enumerate(last):
if l in b:
if b.index(l) < len(rest):
last[i] = rest[b.index(l)]
print(last)

You can try to do something like this...
rest_change_index = 0
for i in range(len(b)):
if b[i] in last:
last_change_index = last.index(b[i])
last[last_change_index] = rest[rest_change_index]
rest_change_index += 1
print(last)
This iterates through the elements of b, and if an element in last matches the element of b being iterated through in the loop, then it changes that value with the corresponding element of rest (first element of rest for first matching instance, etc.). Let me know if this makes sense.

You can do this as follows:
# Get first 4 items in b
b4 = b[:4]
# Create mapping from b to rest
b_to_rest = dict(zip(b4, rest))
# Use dictionary .get to either replace if in rest or keep if not
last = [b_to_rest.get(x,x) for x in last]
Firstly, I've defined b4 as the first 4 items in b. Then I've used zip and dict to map b values to rest values. Finally I use a list comprehension to replace the items in last. .get(x,x) means try to get x from the dictionary, if it doesn't exist just use x.

Related

Append list to a row

I have List Called my_list have series of No I want add that no to a excel's perticuler row
I want list's No start from E2 to len(my_list)
And row is a single and start from E2 to AH2
elements will place in E2 to AH2 likewise image
Click On Here To see Image
wb = load_workbook("Myworkbook.xlsx")
ws = wb.active
my_list=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
for rw in ws['E2:AH2']:
for cell in rw:
for c in my_list:
cell.value = c
wb.save('Myworkbook.xlsx')
But In this way I just get last element of list like in image it gives only 31 but i want 1 to 31
in range and append in particularly in that single row
Click On Here To See image
This is because your last loop (over my_list) goes through the entire list, up to 31, for each cell of your row. That's why you end up with the last value in every cell. You have nested this loop too deep.
What you actually want is go through each cell and value at the same time. Simplest way to achieve that is to zip both lists together and loop over that.
wb = load_workbook("Myworkbook.xlsx")
ws = wb.active
my_list=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
for rw in ws['E2:AH2']:
for cell, c in zip(rw, my_list):
cell.value = c
wb.save('Myworkbook.xlsx')
In your case for each cell the my_list is traversed to the end (by innermost for loop) having each cell assigned with last value, while it's needed to map cells and my_list values pairwise:
To fix that you can apply the following:
for row in ws['E2:AH2']:
for i, cell in enumerate(row):
cell.value = my_list[i]

Return the index of the first element in the list from where incremental increase starts

Suppose I have a list like this, where numbers increase in different steps:
[ 0, 4, 6, 8, 12, 15, 19, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
I want to return the index for the first element in the list where the increase is incremental (+1 step only). In this case, 23 is the first location from which point the increase becomes incremental, and its index would be 8, which is what I want as an output.
What would be an elegant simple way to achieve this? This is what I have tried:
>>> for (a,b) in zip(l, l[1:]):
... if b-a == 1:
... print(l.index(a))
... break
UPDATE: In this particular setup, once the increase becomes incremental it will continue to stay that way. It is possible that the increase will never become incremental.
Solution 1: operator
from operator import sub, indexOf
L = [ 0, 4, 6, 8, 12, 15, 19, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
print(indexOf(map(sub, L[1:], L), 1))
# prints 8
Raises ValueError: sequence.index(x): x not in sequence if difference 1 never occurs, so might want to use try/except for that.
Solution 2: bisect
This one only takes O(log n) time, using the monotonicity of incrementalness (as you commented "once the increase becomes incremental it will continue to stay that way").
from bisect import bisect
L = [ 0, 4, 6, 8, 12, 15, 19, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
class IsIncremental:
def __getitem__(_, i):
return L[i+1] - L[i] == 1
print(bisect(IsIncremental(), False, 0, len(L) - 1))
# prints 8
Prints len(L) - 1 if difference 1 never occurs.
Btw... readability
As PEP 8 says:
Never use the characters 'l' (lowercase letter el), [...] as single character variable names. In some fonts, these characters are indistinguishable from the numerals one and zero. When tempted to use 'l', use 'L' instead.
Steps:
Iterate over the array until the second last element.
Check if next element value differs from current element value by exactly 1.
Print the index and break the loop.
Code:
my_list = [0, 4, 6, 8, 12, 15, 19, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
for i in range(len(my_list)-1):
if my_list[i+1] - my_list[i] == 1:
print(i)
break
Result:
8
Here is an iterative approach. We can loop over the list and take the following action at each index:
If the current value is one plus the previous, then don't move the incremental index
Otherwise, reset the incremental index to the current position
If we reach the end of the list and we have an incremental index which is earlier than the last position, then we have a potential match.
lst = [0, 4, 6, 8, 12, 15, 19, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
idx = 0
for i in range(1, len(lst)):
if lst[i] != lst[i-1] + 1:
idx = i
if idx < len(lst) - 1:
print("Found index: " + str(idx) + ", value: " + str(lst[idx]))
else:
print("No incremental index found")
This prints:
Found index: 8, value: 23
Do a for each loop and check the previous value with the current. Once you reach a point where your current value is only 1 greater than the previous value, return the index of the previous value in your array:
myList = [ 0, 4, 6, 8, 12, 15, 19, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
lastVal = -1000
for i in myList:
if i - lastVal == 1:
print(myList.index(lastVal)) #will print your desired value's index. If this is in a function, replace print with return
break
lastVal = i
if myList.index(lastVal) == len(myList) - 1:
print("There is no incremental increase in your array")
(edited, replaced return with lastVal, fixed to print the index)
Output:
8
Here is a way to do this using list comprehension
lst = [ 0, 4, 6, 8, 12, 15, 19, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
list2 = [i-1 for i,j in enumerate(lst) if j-lst[i-1]==1]
if len(list2)>0:
print(list2[0])
else:
print('No one up number exists')
Similar to the previous answer.
myList = [0, 4, 6, 8, 12, 15, 19, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
l0 = 0 #suppose that the initial value is 0
for l1 in myList:
increment = l1 - l0
if increment == 1:
print(myList.index(l0)) #if you want to get the second element, use l1 instead.
break #if you want to get all the first elements that has 1 increment, remove break
l0 = l1 #memorize l1

I need to generate x amount of unique lists inside another list

I am able to generate the desired output but i need 10 of them and each list has to be unicue. The best solution i thought of was to create a 2nd function, generate a emty list and populate each element with list from 1st function. The output i got so far is x amount of lists but they are not unique and python gives me error when i try to call on the first function inside the 2nd one.
import random
numbers = list(range(1, 35))
out = []
final = []
print(numbers) # to see all numbers
# returns 7 unique pop'd numbers from list 'numbers' and appends them to list 'out'
def do():
for x in range(7):
out.append(numbers.pop(random.randrange(len(numbers))))
print(sorted(out))
# In other words i want to print output from function do() 10 times but each item in list has to be unique, not the lists themself
def do_ten():
for x in range(10):
final.append(out)
# do() python doesnt like this call
print(sorted(final))
do_ten()
This generates a specific amount of lists, in a list, which contain random numbers from 1 to 100, you can use l and n to control the amount of lists and numbers respectively.
import random
l, n = 3, 5 # The amount of lists and numbers respectively.
lis = [[i for i in random.sample(range(1, 35), n)] for group in range(l)]
print(lis)
Random Output:
[[16, 11, 17, 13, 9], [26, 6, 16, 29, 24], [24, 2, 4, 1, 20]]
You are popping 10 times 7 numbers from a list containing 34 elements (from 1 to 34). This is not possible. You need to have at least 70 elements in your list numbers(for example, from 0 to 69).
This is a solution that should work, based on the code you've already written:
import random
numbers = list(range(0, 70))
final = []
print(numbers) # to see all numbers
# returns a list of 7 unique popped numbers from list 'numbers'
def do():
out = []
for x in range(7):
l = len(numbers)
r = random.randrange(l)
t = numbers.pop(r)
out.append(t)
return out
# Call 10 times do() and add the returned list to 'final'
def do_ten():
for x in range(10):
out = do() # Get result from do()
final.append(out) # Add it to 'final'
do_ten()
print(final)
Does it help:
num_lists = 10
len_list = 10
[list(np.random.randint(1,11,len_list)) for _ in range(num_lists)]
As some people may have different definitin of "uniqueness", you may try:
source_list = range(0, num_lists*len_list,1)
[list(np.random.choice(source_list, len_list, replace=False)) for _ in range(num_lists)]
Pulling 7 of 34 numbers from your numberrange without repeats can be done using random.sample - to ensure you do not get duplicate lists, you can add a tuple of the list to a set and your final result and only add to final if this tuple is not yet in the set:
import random
numbers = range(1, 35) # 1...34
final = []
chosen = set()
while len(final) < 10:
# replace popping numbers with random.sample
one_list = random.sample(numbers, k=7) # 7 numbers out of 34 - no repeats
# create a tuple of this list and only add to final if not yet added
t = tuple(one_list)
if t not in chosen:
chosen.add(t)
final.append(one_list)
print (final)
Output:
[[1, 5, 10, 26, 14, 33, 6],
[3, 11, 1, 30, 7, 21, 18],
[24, 23, 28, 2, 13, 18, 1],
[4, 25, 32, 15, 22, 8, 27],
[32, 9, 10, 16, 17, 26, 12],
[34, 32, 10, 26, 16, 21, 20],
[6, 34, 22, 11, 26, 12, 5],
[29, 17, 25, 15, 3, 6, 5],
[24, 8, 31, 28, 17, 12, 15],
[6, 19, 11, 22, 30, 33, 15]]
If you dont need unique resulting lists, you can simplify this to a one-liner but it might have dupes inside:
final = [random.sample(range(1,11),k=7) for _ in range(10)]

Reversed returning same index if value is the same

This works fine for my purposes, except that when the value (in this case 20) is the same, it will only return the index from the front. Code is as follows, I'm not sure what the workaround is, but I need it to return the index of the value from reversed. I have some floats that would differ, but seem more difficult to work with.
lmt_lst = [-1, 5, 9, 20, 44, 37, 22, 20, 17, 12, 6, 1, -6]
lft_lmt = next(x for x in lmt_lst if x >=20)
rgt_lmt = next(x for x in reversed(lmt_lst) if x >= 20)
lft_idx = lmt_lst.index(lft_lmt)
rgt_lmt = lmt_lst.index(rgt_lmt)
print('Left Limit:', lft_lmt, 'Left Index:', lft_idx)
print('Right Limit:', rgt_lmt, 'Right Index:', rgt_idx)
If you change either of the values of '20' to 21, it works just fine
It does not return any errors, just returns the first index of the value, regardless of reversed
Your lft_lmt and rgt_lmt lists only contain values greater than or equal to 20, so your lists are [20, 44, 37, 22, 20] and [20, 22, 37, 44, 20] respectively.
>>> lmt_lst = [-1, 5, 9, 20, 44, 37, 22, 20, 17, 12, 6, 1, -6]
>>> [x for x in lmt_lst if x >=20]
[20, 44, 37, 22, 20]
>>> [x for x in reversed(lmt_lst) if x >= 20]
[20, 22, 37, 44, 20]
The first item of both of these lists is 20, and so when you search for them (using .index which searches from the beginning of the list) in the initial list you'll get 3 both times (because 20 is found at position 3 no matter how many times you search the list from beginning to end).
To find your right index you want to search the reversed list and account for the result being as if searching the list backwards:
>>> lft_idx = lmt_lst.index(lft_lmt)
>>> lft_idx
3
>>> rgt_idx = len(lmt_lst) - 1 - lmt_lst[::-1].index(rgt_lmt)
>>> rgt_idx
7

extracting numbers from list

I've created a list (which is sorted):
indexlist = [0, 7, 8, 12, 19, 25, 26, 27, 29, 30, 31, 33]
I want to extract the numbers from this list that are at least five away from each other and input them into another list. This is kind of confusing. This is an example of how I want the output:
outlist = [0, 7, 19, 25, 31]
As you can see, none of the numbers are within 5 of each other.
I've tried this method:
for index2 in range(0, len(indexlist) - 1):
if indexlist[index2 + 1] > indexlist[index2] + 5:
outlist.append(indexlist[index2])
However, this gives me this output:
outlist = [0, 12, 19]
Sure, the numbers are at least 5 away, however, I'm missing some needed values.
Any ideas about how I can accomplish this task?
You need to keep track of the last item you added to the list, not just compare to the following value:
In [1]: indexlist = [0, 7, 8, 12, 19, 25, 26, 27, 29, 30, 31, 33]
In [2]: last = -1000 # starting value hopefully low enough :)
In [3]: resultlist = []
In [4]: for item in indexlist:
...: if item > last+5:
...: resultlist.append(item)
...: last = item
...:
In [5]: resultlist
Out[5]: [0, 7, 19, 25, 31]
This should do the trick. Here, as I said in comment, the outlist is initialised with the first value of indexlistand iterated indexlist elements are compared to it. It is a rough solution. But works.
indexlist = [0, 7, 8, 12, 19, 25, 26, 27, 29, 30, 31, 33]
outlist = [indexlist[0]]
for index2 in range(1, len(indexlist) - 1):
if indexlist[index2] > (outlist[-1] + 5):
outlist.append(indexlist[index2])
output:
>>outlist
[0, 7, 19, 25, 31]
Tim Pietzcker's answer is right but this can also be done without storing the last added item in a separate variable. Instead you can read the last value in outlist:
>>> indexlist = [0, 7, 8, 12, 19, 25, 26, 27, 29, 30, 31, 33]
>>> outlist = []
>>> for n in indexlist:
... if not outlist or n > outlist[-1] + 5:
... outlist.append(n)
...
>>> outlist
[0, 7, 19, 25, 31]
I suppose your index_list is sorted. Then this will give you only indexes MIN_INDEX_OFFSET apart.
MIN_INDEX_OFFSET = 5;
index_list = [0, 7, 8, 12, 19, 25, 26, 27, 29, 30, 31, 33];
last_accepted = index_list[0];
out_list = [last_accepted];
for index in index_list:
if index-last_accepted > MIN_INDEX_OFFSET:
out_list.append(index);
last_accepted = index;
print(out_list)

Categories