Adjacent multiple elements removing in Python sequences - python

I have a sequence like [0, 1, 0, 1, 0, 1, 0] and I need a function to remove repeated adjacent sequence pairs, keeping the first one, and return [0, 1, 0]. These are some results I expect.
>>> remove_repeated_pairs([0, 1])
[0, 1]
>>> remove_repeated_pairs([0, 1, 0])
[0, 1, 0]
>>> remove_repeated_pairs([0, 1, 0, 1])
[0, 1]
>>> remove_repeated_pairs([0, 1, 0, 1, 0])
[0, 1, 0]
>>> remove_repeated_pairs([2, 0, 1, 0, 1, 0])
[2, 0, 1, 0]
>>> remove_repeated_pairs([1, 2, 0, 1, 0, 1, 0])
[1, 2, 0, 1, 0]
first edition:
I tried this code:
def remove_repeated_pairs(seq):
result = []
for i in range(0, len(seq), 2):
if len(result) >= 2:
last_seq = result[-2:]
else:
last_seq = None
pair = seq[i:i + 2]
if pair != last_seq:
result.extend(pair)
return result
But it doesn't works with this:
>>> remove_repeated_pairs([1, 3, 0, 2, 1, 2, 1, 3, 0])
[1, 3, 0, 2, 1, 2, 1, 3, 0]
The right answer should be [1, 3, 0, 2, 1, 3, 0]

I think that the issue comes from the fact that you go over the elements of your list 2 by 2 (for i in range(0, len(seq), 2).
So if a repeated pair starts on an odd place, you won't detect it - as in the last example you give.
I would try something like:
def remove_repeated_pairs(l):
i = 2;
while i < len(l)-1:
if l[i] == l[i-2] and l[i+1]==l[i-1]:
l.pop(i);
l.pop(i);
else:
i+=1;
return l;
Regards,

Here's version that works with an arbitrary iterable, not just sequences:
def remove_repeated_pairs(iterable):
it = iter(iterable)
a = next(it) # always yield the first pair
yield a
b = next(it)
yield b
c = next(it)
for d in it:
if a != c or b != d:
yield c
a, b, c = b, c, d # shift by one item
else: # repeated pair, skip it
a, b, c = c, d, next(it)
yield c
Example
>>> list(remove_repeated_pairs([1, 3, 0, 2, 1, 2, 1, 3, 0]))
[1, 3, 0, 2, 1, 3, 0]

Here is a more concise version:
def remove_repeated_pairs(seq):
pairs = zip([-1] + seq,seq)[1:]
l = [index for (index,pair) in enumerate(pairs) if (index > 1) and
(pair == pairs[index-2])]
return [seq[x] for x in range(len(seq)) if x not in l and x+1 not in l ]
print remove_repeated_pairs([1, 3, 0, 2, 1, 2, 1, 3, 0])
#OUTPUT: [1, 3, 0, 2, 1, 3, 0]
print remove_repeated_pairs([1, 2, 4, 1, 4, 1, 3])
#OUTPUT: [1, 2, 4, 1, 3]

You have to check pairs starting at odd indices as well, i.e. pairs seq[1:2] equals seq[3:4] as well as even ones, i.e. seq[0:1] equals seq[2:3]

def remove_repeated_pairs(seq):
ret = [seq[0]]
s = None
i = 1
while i < len(seq)-1:
pair = (seq[i], seq[i+1])
if pair != s:
s = (seq[i-1], seq[i])
ret.append(seq[i])
else:
i+=1
i+=1
if i == len(seq)-1:
ret.append(seq[i])
return ret

You need to move forward one by one instead of two by two
def remove_repeated_pairs(l):
if(len(l) < 4):
return l
result = l[:2]
i = 2
while i < (len(l)-1):
if l[i] == result[-2] and l[i+1] == result[-1]:
i += 2
else:
result.append(l[i])
i += 1
result += l[i:]
return result

Related

Replacing items in a list with items from another list in python

list1 = [3,4,1,1,0,3,1,0,4,3,3,2,3]
list2 = [0,2,1,0,5]
Would it be possible to replace each 3 in list1 with an element from list2? If so, how would I go about doing this?
You could create an iterator for list2, and then call next() to replace the next element in list1 that is equal to 3, while using a list comprehension.
list1 = [3,4,1,1,0,3,1,0,4,3,3,2,3]
list2 = [0,2,1,0,5]
l2 = iter(list2)
out = [i if i!=3 else next(l2) for i in list1]
print(out)
Output:
[0, 4, 1, 1, 0, 2, 1, 0, 4, 1, 0, 2, 5]
We can use two pointers to keep track of list1 and list2. Then we replace each 3 in list1 with a value from list2 even it is not 0 until traversing to the end of any of the list.
list1 = [3, 4, 1, 1, 0, 3, 1, 0, 4, 3, 3, 2, 3]
list2 = [0, 0, 0, 0, 0]
x = 0
y = 0
while x < len(list1) and y < len(list2):
if list1[x] == 3:
list1[x] = list2[y]
y += 1
x += 1
print(list1)
Output:
[0, 4, 1, 1, 0, 0, 1, 0, 4, 0, 0, 2, 0]
list1 = [3,4,1,1,0,3,1,0,4,3,3,2,3]
list2 = [0,2,1,0,5]
l1_num = 3 # number to be replaced in list 1
l2_num = 0 # number to be replaced in list 2
n = min(list1.count(l1_num),list2.count(l2_num))
for i in range(n):
list1[list1.index(l1_num)] = l2_num
list2[list2.index(l2_num)] = l1_num
#list1 = [0, 4, 1, 1, 0, 0, 1, 0, 4, 3, 3, 2, 3]
#list2 = [3, 2, 1, 3, 5]
we can use for in and store our needed short list value out of the loop. like the following code will replace all 3 in list1 with zeros and all 0 in list2 with 3s in list1:
list1 = [3,4,1,1,0,3,1,0,4,3,3,2,3]
list2 = [0,1,0,3,0]
#shortlistval will be used to store short list desired value
shortlistVal =1;#default val is just for declaration
for i1 in range(0, len(list1)):
for i in range(0,len(list2)):
if list1[i1] ==3 and list2[i] == 0:
shortlistVal=list2[i]
list2[i]=list1[i1]
if list1[i1]==3:
list1[i1]= shortlistVal
print(list1)
print(list2)
Output:
[0, 4, 1, 1, 0, 0, 1, 0, 4, 0, 0, 2, 0]
[3, 1, 3, 3, 3]
Use two for loops and replace each 3 for 0 s in list 2.
list1 = [3,4,1,1,0,3,1,0,4,3,3,2,3]
list2 = [0,2,1,0,5]
for i in range(list2.count(0)):
for x in list1:
if x == 3: list1[list1.index(x)] = 0; break
print(list1)
output:
[0, 4, 1, 1, 0, 0, 1, 0, 4, 3, 3, 2, 3]

Why the iteration variable changed without `return` or `yield` in Python3

I'm a newer to Python and I'm learning it.
Here I want to use a generator to output the Pascal's Triangle. I'd like to get the output like this
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
And what I wrote is
# Pascal's Triangle
def triangles() :
result = [1]
while True :
# if i use yield result, the output will be [1, 0], [1, 1, 0] ...
yield result[:]
result.append(0)
result = [result[i]+result[i-1] for i in range(len(result))]
# make a list of the triangles.
n = 0
results = []
for t in triangles():
results.append(t)
n = n + 1
if n == 5:
break
for t in results:
print(t)
This code works well. However when I change yield result[:] to yield result, the output is
[1, 0]
[1, 1, 0]
[1, 2, 1, 0]
[1, 3, 3, 1, 0]
[1, 4, 6, 4, 1]
After debugging the code, I found that the iteration variable t changed to [1, 0] after running
result.append(0)
the first time, but there isn't any words like return or yield.
Why could that happen?
Update Question
I made a minimal working example here
def func():
a = [1, 2]
while True :
yield a
a.append(0)
# # PM 1 (Print Method 1)
# n = 0
# results = []
# b = func()
# for t in b:
# results.append(t)
# n = n + 1
# if n == 3:
# break
# for t in results:
# print(t)
# PM 2
b = func()
counter = 0
for i in b :
print(i)
counter += 1
if counter == 3:
break
When I use PM 1 to print the list, I got
[1, 2, 0, 0]
[1, 2, 0, 0]
[1, 2, 0, 0]
When I use PM 2 to print the list, I got
[1, 2]
[1, 2, 0]
[1, 2, 0, 0]
So I think the problem occurs at how I print the list.
I am sure what is the direction of your work, whether it just for print or more. But, below should give you a general overview about tril_indices
import numpy as np
# Pascal's Triangle
def triangles() :
result = [1]
while True :
# if i use yield result, the output will be [1, 0], [1, 1, 0] ...
yield result[:]
result.append(0)
result = [result[i]+result[i-1] for i in range(len(result))]
# make a list of the triangles.
n = 0
results = []
for t in triangles():
results.extend(t)
n = n + 1
if n == 5:
break
res_arr=np.array(results)
t,b = np.tril_indices(6, -1)
idx_pair = np.tril_indices(6, -1)
this_con = np.zeros ( (6, 6) )
this_con [idx_pair] = res_arr
print(this_con)

How sort specific items in specific order in a list

Hello everybody I am trying to do an algorithm that sort specific items in a specifig order.
Let's assume that we have a list of items :
[1, 2, 1, 3, 3, 3, 2, 2, 2, 2, 2, 1, 3, 2, 2, 1]
there is three different type of items here : 1, 2 and 3. I want to sort this list in order to have a sequence of items that follows the same types. In the sorting process we can't move the position's items, we just can remove some items and the result should be the longest.
The result of this algorithm should be :
[1, 1, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2]
I don't know why my algorithm doesn't work :
# start list
givenList = [1, 2, 1, 3, 3, 3, 2, 2, 2, 2, 2, 1, 3, 2, 2,1]
# whish list (just for an example)
whish = [1, 1, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2]
# list grouped by items ( [ [1], [2], [1], [3, 3, 3], [2, 2, 2, 2, 2] ....])
sortedList = []
# we group the elements
lastElement=0
for currentElement in givenList :
if currentElement != lastElement :
sortedList.append([currentElement])
lastElement=currentElement
else : sortedList[-1].append(currentElement)
# we print the grouped items
for index, element in enumerate(sortedList) :
print("Bloc : ", index , " contient : " , element)
# we sort the same elements by group
result=[]
for index, element in enumerate(sortedList) :
# we pass if it's the first group because he has no backward element
if(index == 0) : continue
# we pass if it's the last group because he has no afterward element
if(index == len(sortedList) - 1) : continue
# backward group
backwardList = sortedList[index - 1]
# current group
currentList = sortedList[index]
# afterward group
forwardList = sortedList[index + 1]
# if the backward groupelement type is the same as the forward
if backwardList[0] == forwardList[0] :
# and if the backwardlist contains more element that the current group
if(len(backwardList) >= len(currentList)) :
# we add the concatenation of the backwards and the forwards group
result.append(backwardList + forwardList)
elif backwardList[0] != forwardList[0] :
# else we just add the current group
result.append(currentList)
# we degroup the grouped and sorted list
resultSorted=[]
for e in result:
for i in e:
resultSorted.append(i)
#
print("#"*20)
print("Given : ", givenList)
print("Whish : ", whish)
print("Result : ", resultSorted)
print("#"*20)
You can try this implementation with itertools.groupby:
from itertools import groupby
l = [1, 2, 1, 3, 3, 3, 2, 2, 2, 2, 2, 1, 3, 2, 2, 1]
def get_max(elems, current_elems=None):
if current_elems is None:
current_elems = []
for idx, (color, count) in enumerate(elems):
if color in [c for c, _ in current_elems[:-1]]:
continue
yield current_elems + [(color, count)]
yield from get_max( elems[idx+1:], current_elems + [(color, count)] )
elems = [(v, sum(1 for _ in g)) for v, g in groupby(l)]
l, _ = max(((v, sum(c for _, c in v)) for v in get_max(elems)), key=lambda k: k[1], default=[[], None])
out = []
for v, g in groupby(l, key=lambda k: k[0]):
for _, c in g:
out.extend([v] * c)
print(out)
Prints:
[1, 1, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2]
Other test cases:
l = [1, 2, 3] # [1, 2, 3]
l = [1, 2, 3, 1, 1] # [2, 3, 1, 1]
The best solution is :
def solve(l):
def aux(i, current, exclude):
if i==len(l):
return []
val = l[i]
if val in exclude:
return aux(i+1, current, exclude)
elif val == current:
return [val] + aux(i+1, current, exclude)
else:
exclude.add(current)
s1 = [val] + aux(i+1, val, exclude)
exclude.remove(current)
s2 = aux(i+1, current, exclude)
if len(s1)>len(s2):
return s1
return s2
return aux(0, -1, set())

how to find the count the difference back to the previous zero in a list?

I have below list
[7, 2, 0, 3, 4, 2, 5, 0, 3, 4]
and the result list is
[1, 2, 0, 1, 2, 3, 4, 0, 1, 2]
the result obtained by with the below logic
For each value, count the difference back to the previous zero (or the start of the Series,
whichever is closer).
am trying to implement , but not able to get .
So how to find the previous zero position , such that we can get that series ?
I tried below , but somehow it is failing , and it is seems not good solution
import pandas as pd
df = pd.DataFrame({'X': [7, 2, 0, 3, 4, 2, 5, 0, 3, 4]})
#print(df)
df_series = df['X']
print(df_series.iloc[-1])
target_series = pd.Series([])
#print(target_series)
def calculate_value(i,actual_index):
if(df_series.iloc[i-1] == 0):
if(i < 0):
zero_position = i + df_series.size-1
if(actual_index - 0 < zero_position):
target_series[actual_index]=actual_index+1
return
else:
target_series[actual_index]=zero_position
return
else:
target_series[actual_index]=target_series[actual_index]+1
return
else:
if(i+df_series.size != actual_index):
calculate_value(i-1,actual_index)
for i in df.index:
if(df_series[i]==0 and i!=0):
target_series[i]=0
elif(df_series[i]!=0 and i==0):
target_series[i]=1
else:
calculate_value(i,i)
print(target_series)
If you want a Pandas one-liner solution:
import pandas as pd
s = pd.Series([7, 2, 0, 3, 4, 2, 5, 0, 3, 4])
(s.groupby(s.eq(0).cumsum().mask(s.eq(0))).cumcount() + 1).mask(s.eq(0), 0).tolist()
Output:
[1, 2, 0, 1, 2, 3, 4, 0, 1, 2]
If you stick to the list you can get your result quite easily:
l = [7, 2, 0, 3, 4, 2, 5, 0, 3, 4]
i = 0
r = []
for element in l:
if element != 0:
i += 1
else:
i = 0
r.append(i)
r
#[1, 2, 0, 1, 2, 3, 4, 0, 1, 2]
Here is a working solution for you:
a = [7, 2, 0, 3, 4, 2, 5, 0, 3, 4]
b = []
z_pos = -1
for i, n in enumerate(a):
if n == 0:
z_pos = i
b.append(i - z_pos)
print(b) # [1, 2, 0, 1, 2, 3, 4, 0, 1, 2]
It does not use anything too fancy so I explaining its internal workings is unnecessary I think. If however there is something that is not clear to you let me know.
If you want to use python only, here is the solution:
a = [7, 2, 0, 3, 4, 2, 5, 0, 3, 4]
z = None
b = []
for i in range(len(a)):
if a[i] != 0 and z== None:
b.append(i+1)
elif a[i] == 0:
b.append(0)
z = 0
else:
z += 1
b.append(z)
b is the required list.
Here is how to do it with a standard list, not Pandas dataframe, but the logic is the same
arr = [7,2,0,3,4,5,1,0,2]
arr2 = []
counter = 1
for item in arr:
if(item==0):
counter = 0
arr2.append(counter)
counter+=1
print(arr2)
You can see the code working here.
Some explanations:
What you want is a sort of counter between two 0 in your array.
So you loop over your array, and each time you encounter a 0 you reset your counter, and you insert the value of your counter each iteration of the loop.
If you are a fan of shortness instead of readability or effectiveness, you can also roll with this:
a = [7, 2, 0, 3, 4, 2, 5, 0, 3, 4]
distances = [([0]+a)[:idx][::-1].index(0) for idx in range(1, len(a)+2)][1:]
Which gives the desired result:
print(distances)
>> [1, 2, 0, 1, 2, 3, 4, 0, 1, 2]
Solution with no for loops or apply, just some pandas groupby fun.
We use
df = pd.DataFrame({'X': [7, 2, 0, 3, 4, 2, 5, 0, 3, 4]})
# make a new column with zeros at zeros and nans elsewhere
df = df.assign(idx_from_0=df.loc[df.X==0])
nul = df['idx_from_0'].isnull()
df.assign(idx_from_0=nul.groupby((nul.diff() == 1).cumsum()).cumsum())
Out[1]:
X idx_from_0
0 7 1.0
1 2 2.0
2 0 0.0
3 3 1.0
4 4 2.0
5 2 3.0
6 5 4.0
7 0 0.0
8 3 1.0
9 4 2.0
The cumsum forward fill add one was taken from this answer.

Python - alternating lists

I have two lists in python:
l = [1,1,1,1,1,1]
b = ['-', 2, 2, 2, '-', 2]
In the end, I'd like to have a list like this:
result = [1, 1, 2, 1, 2, 1, 2, 1, 1, 2]
Algorithm:
If there is a '-' in b, do nothing, else append element from b after element in l at same index (Second 2 from b should go after second 1 in l). How can I do this?
for idx, i2 in enumerate(b):
if i2 != '-' and count == 1:
l.insert(prev+2,i2)
prev = prev+2
print "in if1"
print l
print prev
elif i2 != '-' and count == 0:
l.insert(idx+1,i2)
prev = idx+2
count = 1
print "in if2"
print l
print prev
l, b = [1,1,1,1,1,1], ['-', 2, 2, 2, '-', 2]
print [item for items in zip(l, b) for item in items if item != '-']
Output
[1, 1, 2, 1, 2, 1, 2, 1, 1, 2]
If the number of elements in either of the lists is not going to be equal to the other, you can use itertools.izip_longest like this
l, b = [1,1,1,1,1,1], ['-', 2, 2, 2, '-', 2, 2, 2]
from itertools import izip_longest
print [e for items in izip_longest(l,b) for e in items if e != None and e != '-']
Output
[1, 1, 2, 1, 2, 1, 2, 1, 1, 2, 2, 2]
You can used "chained" list comprehenstion (i.e. with two for loops), and a filter (x!='-'):
l1 = [1,1,1,1,1,1]
l2 = ['-', 2, 2, 2, '-', 2]
[ x for (a,b) in zip(l1,l2) for x in (a,b) if x != '-' ]
=> [1, 1, 2, 1, 2, 1, 2, 1, 1, 2]

Categories

Resources