Split a list into various slices and concatenate it - python

I have seen similar questions to mine, but nothing I researched really fixed my issue.
So, basically I want to split a list, in order to remove some items and concatenate it back. Those items correspond to indexes that are given by a list of tuples.
import numpy as np
arr = ['x','y','z','a','b','c','d','e','f','g',2,3,4]
indices = [(2,4),(7,9)] #INDEXES THAT NEED TO BE CUT OUT
print ([list1[0:s] +list1[s+1:e] for s,e in indices])
#Returns: [['x', 'y', 'z', 'a'], ['x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f']]
This code I have, which I got from one of the answers from this post nearly does what I need, but I tried to adapt it to loop over the first index of indices once but instead it does twice and it doesn't include the rest of the list.
I want my final list to split from zero index to the first item on first tuple and so on, using a for loop or some iterator.
Something like this,
`final_arr = arr[0:indices[0][0]] + arr[indices[0][1]:indices[1][0]] + arr[indices[1][1]:]<br/>
#Returns: [['x','y','a','b','c','f','g',2,3,4]]`
If someone could do it using for loops, it would be easier for me to see how you understand the problem, then after I can try to adapt to using shorter code.

Sort the indices using sorted and del the slices. You need reverse=True otherwise the indices of the later slices are incorrect.
for x, y in sorted(indices, reverse=True):
del(arr[x:y])
print(arr)
>>> ['x', 'y', 'b', 'c', 'd', 'g', 2, 3, 4]
This is the same result as you get with
print(arr[0:indices[0][0]] + arr[indices[0][1]:indices[1][0]] + arr[indices[1][1]:])
>>> ['x', 'y', 'b', 'c', 'd', 'g', 2, 3, 4]

arr = ['x','y','z','a','b','c','d','e','f','g',2,3,4]
indices = [(2,4),(7,9)] #INDEXES THAT NEED TO BE CUT OUT
import itertools
ignore = set(itertools.chain.from_iterable(map(lambda i: range(*i), indices)))
out = [c for idx, c in enumerate(arr) if idx not in ignore]
print(out)
print(arr[0:indices[0][0]] + arr[indices[0][1]:indices[1][0]] + arr[indices[1][1]:])
Output,
['x', 'y', 'b', 'c', 'd', 'g', 2, 3, 4]
['x', 'y', 'b', 'c', 'd', 'g', 2, 3, 4]

Like this:
import numpy as np
arr = ['x','y','z','a','b','c','d','e','f','g',2,3,4]
indices = [(2,4),(7,9)] #INDEXES THAT NEED TO BE CUT OUT
print ([v for t in indices for i,v in enumerate(arr) if i not in range(t[0],t[1])])
Output:
['x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 2, 3, 4, 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 2, 3, 4]

1- If you can remove the list items:
I using the example for JimithyPicker. I change the index list (removed items), because always that one index was removed the size of list change.
arr = ['x','y','z','a','b','c','d','e','f','g',2,3,4]
indices = [2,5,5] #INDEXES THAT NEED TO BE CUT OUT
for index in indices:
arr.pop(index)
final_arr = [arr]
print(final_arr)
Output:
[['x', 'y', 'a', 'b', 'c', 'f', 'g', 2, 3, 4]]
2- If you can't remove items:
In this case is necessary change the second index! The number doesn't match with output that you want.
The indices = [(2,4),(7,9)] has the output: ['x', 'y', 'a', 'b', 'c', 'd', 'f', 'g', 2, 3, 4]
arr = ['x','y','z','a','b','c','d','e','f','g',2,3,4]
indices = [(2,4),(6,9)] #INDEXES THAT NEED TO BE CUT OUT
final_arr = arr[0:indices[0][0]] + arr[indices[0][1]-1:indices[1][0]] + arr[indices[1][1]-1:]
print(final_arr)
Output:
['x','y','a','b','c','f','g',2,3,4]

Related

Filter list using duplicates in another list

I have two equal-length lists, a and b:
a = [1, 1, 2, 4, 5, 5, 5, 6, 1]
b = ['a','b','c','d','e','f','g','h', 'i']
I would like to keep only those elements from b, which correspond to an element in a appearing for the first time. Expected result:
result = ['a', 'c', 'd', 'e', 'h']
One way of reaching this result:
result = [each for index, each in enumerate(b) if a[index] not in a[:index]]
# result will be ['a', 'c', 'd', 'e', 'h']
Another way, invoking Pandas:
import pandas as pd
df = pd.DataFrame(dict(a=a,b=b))
result = list(df.b[~df.a.duplicated()])
# result will be ['a', 'c', 'd', 'e', 'h']
Is there a more efficient way of doing this for large a and b?
You could try if this is faster:
firsts = {}
result = [firsts.setdefault(x, y) for x, y in zip(a, b) if x not in firsts]

Is there a way to get all permutations with the condition of unique adjacent elements?

I have the following Data, each letter of the Alphabet:
Letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
My goal is to get the maximum number of permutations of a length of 5, with the following 3 conditions:
No letter shall be repeated more than once in each permutation.
No permutation shall share more than 3 letters in common with another permutation in any position.
No permutation shall share more than 2 adjacent letters in common in an identical position with another permutation.
e.g. Permutations ['A', 'B', 'C', 'D', 'E'] and ['A', 'B', 'F', 'G', 'H'] should not be permitted as the same 'A' and 'B' are adjacent in both permutations. However, permutations ['A', 'B', 'C', 'D', 'E'] and ['A', 'F', 'B', 'G', 'H'] should be permitted.
Each permutation has 5 elements, [Element 1, Element 2, Element 3, Element 4, Element 5]. The neighbours of Element 1 is only Element 2. The neighbours of Element 2 is Element 1 and Element 3. For each permutation the neighbouring pairs are: [1, 2], [2, 3], [3, 4], [4, 5]. No permutation should have the same neighbouring pair as another IF and only if they are in the same element position.
Another similar example: ['A', 'B', 'C', 'D', 'E'] and ['A', 'B', 'F', 'G', 'H']. In the neighbouring pair of both permutations, [Element 1, Element 2] is ['A', 'B']. As they are identical at the same Element location, the solution is not valid.
If however: ['A', 'B', 'C', 'D', 'E'] and ['F', 'A', 'B', 'G', 'H']. In this example, although they both have a neighbouring pair ['A', 'B'], they are found in different Element locations [Element 1, Element 2] and [Element 2, Element 3] respectively. Therefore, they are both valid for the solution.
I have tried two different approaches to solve this task - using itertools with if conditions and mixed integer programming.
In terms of using itertools, I was not able to successfully define the conditions. I tried the following style of approach, with no luck:
from itertools import permutations
AllPermutations = list(permutations(Letters, 5))
ActualPermutations = []
for Permutation in AllPermutations:
if Permutation[0:2] not in [Permutation[0:2] for Permutation in ActualPermutations]:
ActualPermutations.append(Permutation)
While with mixed integer programming, I was not able to wrap my head around the objective function, as I was not minimizing or maximizing anything. Furthermore, mixed integer programming would only give me 1 permutation that fits with the constraints.
Can you please check if that solution matches your expectations? If so, I can provide some explanation.
from itertools import combinations
def three_letters_sub_set(letters):
return combinations(letters, 3)
def adjacent_letters_sub_set(letters):
for position in range(len(letters) - 1):
adjacent_letters = (letters[position], letters[position + 1])
yield position, adjacent_letters
def fails_rule2(letters):
return any(
three_letters in existing_three_letter_tuples
for three_letters in three_letters_sub_set(letters)
)
def fails_rule3(letters):
for position, adjacent_letters in adjacent_letters_sub_set(letters):
if adjacent_letters in existing_adjacent_letters[position]:
return True
return False
n_letters = 5
existing_three_letter_tuples = set()
existing_adjacent_letters = {position: set() for position in range(n_letters - 1)}
for comb in combinations(Letters, n_letters):
if fails_rule2(comb):
continue
if fails_rule3(comb):
continue
existing_three_letter_tuples.update(three_letters_sub_set(comb))
for position, adjacent_letters in adjacent_letters_sub_set(comb):
existing_adjacent_letters[position].add(adjacent_letters)
print(comb)

Get right label using indices?

Really stupid question as I am new to python:
If I have labels = ['a', 'b', 'c', 'd'],
and indics = [2, 3, 0, 1]
How should I get the corresponding label using each index so I can get: ['c', 'd', 'a', 'b']?
There are a few alternatives, one, is to use a list comprehension:
labels = ['a', 'b', 'c', 'd']
indices = [2, 3, 0, 1]
result = [labels[i] for i in indices]
print(result)
Output
['c', 'd', 'a', 'b']
Basically iterate over each index and fetch the item at that position. The above is equivalent to the following for loop:
result = []
for i in indices:
result.append(labels[i])
A third option is to use operator.itemgetter:
from operator import itemgetter
labels = ['a', 'b', 'c', 'd']
indices = [2, 3, 0, 1]
result = list(itemgetter(*indices)(labels))
print(result)
Output
['c', 'd', 'a', 'b']

How can I apply a permutation to a list?

How one might get Sympy Permutation to act on a list? E.g.,
from sympy.combinatorics import Permutation
lst = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
perm = Permutation([[0, 2, 8, 6], [1, 5, 7, 3]])
# Then something like...
perm * lst # This doesn't work. Throws AttributeError because of list
I'd like something like this that returns (in this example):
['g', 'd', 'a', 'h', 'e', 'b', 'i', 'f', 'c']
I have read https://docs.sympy.org/latest/modules/combinatorics/permutations.html, and don't see how.
Any suggestions as to how might one go about this?
You can just do perm(lst)
>>> from sympy.combinatorics import Permutation
>>> lst = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
>>> perm = Permutation([[0, 2, 8, 6], [1, 5, 7, 3]])
>>> perm(lst)
['c', 'f', 'i', 'b', 'e', 'h', 'a', 'd', 'g']
Your example output seems to have the result of applying the reverse of the given Permutation to the list - if that is your required output you need to either reverse the final list or each list within the permutation.
From here:
The permutation can be ‘applied’ to any list-like object, not only Permutations.

How to randomly replace elements of list with another list elements?

x_list = ['a', 'b', 'c', 'd', 'e']
t_list = ['z', 'y', 'u']
I want replace elements of t_list with elements of x_list, "randomly". I researched and I tried something but replace() didn't work for lists. For example; x_new0 = ['a', 'z', 'c', 'y', 'u], x_new1 = ['z', 'y', 'c', 'd','u']. How can I do this?
You can use the random module and use the random.randint() method to generate a random number between 1 and 100 as a heads and tails coin toss to decide whether to replace the value or not and then use the random.choice() method on the 2nd list to return a random element of the list
import random
first = ['a', 'b', 'c', 'd', 'e']
second = ['z', 'y', 'u']
print(first)
for index, x in enumerate(first):
if random.randint(0, 1):
first[index] = random.choice(second)
print(first)
First run stdout:
>> ['a', 'b', 'c', 'd', 'e']
>> ['y', 'z', 'u', 'y', 'y']
Second run stdout:
>> ['a', 'b', 'c', 'd', 'e']
>> ['a', 'u', 'c', 'u', 'y']
You could use numpy.random.choice to randomly select elements where you want the replacements to occur in the original list. Then zip those indexes against the values you want to use for the substitution and apply the replacements.
from numpy.random import choice
def swap_elements(x, t):
new_x = x[:]
for idx, value in zip(choice(range(len(x)), size=len(t), replace=False), t_list):
new_x[idx] = value
return new_x
Example usage
>>> x_list = ['a', 'b', 'c', 'd', 'e']
>>> t_list = ['z', 'y', 'u']
>>> swap_elements(x_list, t_list)
['y', 'u', 'z', 'd', 'e']
>>> swap_elements(x_list, t_list)
['y', 'b', 'z', 'u', 'e']
>>> swap_elements(x_list, t_list)
['y', 'b', 'z', 'u', 'e']
>>> swap_elements(x_list, t_list)
['a', 'u', 'z', 'y', 'e']
You can choose a random index of one list, and a random index of the other list and take the value at the index of one and place it at the index of the other. For example:
from random import randint
a = [1 ,2, 3, 4]
b = [5, 6, 7, 8]
RandomIndexA = randint(0, len(a) - 1)
RandomIndexB = randint(0, len(b) - 1)
a[RandomIndexA] = b[RandomIndexB]
Alternatively, you could only calculate one random index and then randomly choose an element from the other list using random.choice(b) for example:
from random import randint, choice
a = [1 ,2, 3, 4]
b = [5, 6, 7, 8]
RandomIndex = randint(0, len(a) - 1)
a[RandomIndex] = choice(b)

Categories

Resources