Remove certain indexes in a list - python

Suppose I have a list filled with indexes to remove
remove = [0, 2, 4, 5, 7, 9, 10, 11]
Then I have another list of lists, such as
l = [['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'], ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l']]
I want to remove the values at the indexes in remove

If you don't have to do this in place, you can construct new lists based on the index:
[[v for i, v in enumerate(s) if i not in to_remove] for s in l]
# [['b', 'd', 'g', 'i'], ['b', 'd', 'g', 'i']]

If you perform a step by step execution, the problem will become evident.
As you remove elements, the position of the following elements changes. For example, if you remove element 0 from a list, what was element 1 will become element 0.
If you want to stick with the current approach, just traverse the indices in reverse order (you don't need the values, just use a range).

If you don't want list comprehension, you can use a couple of loops like so:
for x in remove[::-1]:
for list in l:
del list[x]
[['b', 'd', 'g', 'i'], ['b', 'd', 'g', 'i']]

Related

How do I record the history of what happened while using sort in python for one string, and apply that to other strings?

with open(sys.argv[1]) as f:
lst = list(f.readline().strip())
sortedLst = sorted(lst, key = lambda x: (x.lower(), x.swapcase()))
print(lst)
print(sortedLst)
The word I am using as an example is 'ThatCcer'.
My outputs are ['T', 'h', 'a', 't', 'C', 'c', 'e', 'r'] for lst and my outputs are ['a', 'c', 'C', 'e', 'h', 'r', 't', 'T'] for sortedLst.
This is exactly what I am going for - to sort a word in alphabetical order with lower case letters taking precedence over upper case.
What I am trying to achieve is to match other 8-letter inputs by sorting them in the exact way that I have sorted ThatCcher. How would I go about achieving this?
EDIT: I am being told the question is unclear - my apologies but it is a bit difficult to explain so I will try again.
By sorting ThatCcer to become acCehrtT, lst[0] ('T') took the position of sortedLst[7], lst[1] ('h') took the position of sortedLst[4], and so on...
This is the history I want to record and so that given any other string can copy the steps that 'ThatCcer' took, for example: s = ['h', 'o', 'w', 'e', 'v', 'e', 'r', 's'] I want s[0] to to take its' position in sortedS[7], just like ThatCcer did.
I hope this made it a little clearer!
IIUC, you want to achieve a behavior similar to that of numpy.argsort.
You can sort a range based on your criteria, and use it to reindex any string:
lst = ['T', 'h', 'a', 't', 'C', 'c', 'e', 'r']
idx = list(range(len(lst)))
sorted_idx = sorted(idx, key=lambda x: (lst[x].lower(), lst[x].swapcase()))
# [2, 5, 4, 6, 1, 7, 3, 0]
# now use the index to sort
[lst[i] for i in sorted_idx]
# ['a', 'c', 'C', 'e', 'h', 'r', 't', 'T']
# keep the same order on another string
lst2 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
[lst2[i] for i in sorted_idx]
# ['c', 'f', 'e', 'g', 'b', 'h', 'd', 'a']
Another approach using zip:
lst = ['T', 'h', 'a', 't', 'C', 'c', 'e', 'r']
lst2 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
list(zip(*sorted(zip(lst, lst2), key=lambda x: (x[0].lower(), x[0].swapcase()))))
# or as individual lists
# (lst_sorted, lst2_sorted) = list(zip(*sorted(zip(lst, lst2),
# key=lambda x: # (x[0].lower(), x[0].swapcase()))))
output:
[('a', 'c', 'C', 'e', 'h', 'r', 't', 'T'),
('c', 'f', 'e', 'g', 'b', 'h', 'd', 'a')]
Sort the enumerated string on the string characters, then separate the (sorted) indices and characters; use operator.itemgetter to create a callable that you can re-use.
import operator
def f(thing):
s_lst = sorted(enumerate(thing),key = lambda x: (x[1].lower(), x[1].swapcase()))
argsort = operator.itemgetter(*[x[0] for x in s_lst])
s_lst = [x[1] for x in s_lst]
return s_lst,argsort
>>> s_lst, argsort = f('ThatCcerCTaa')
>>> s_lst
['a', 'a', 'a', 'c', 'C', 'C', 'e', 'h', 'r', 't', 'T', 'T']
>>> argsort('ThatCcerCTaa')
('a', 'a', 'a', 'c', 'C', 'C', 'e', 'h', 'r', 't', 'T', 'T')
>>> argsort
operator.itemgetter(2, 10, 11, 5, 4, 8, 6, 1, 7, 3, 0, 9)
>>>

Python - Creating permutations with output array index constraints

I want to create all possible permutations for an array in which each element can only occur once, with constraints on the element array index position.
ID = ["A","B","C","D","E","F","G","H","I","J"]
I want to create all possible permutations of the original_array, however the positions of each element are restricted to index positions given by:
ID = ["A","B","C","D","E","F","G","H","I","J"]
Index_Options=[]
for i in range(len(ID)):
List1=[]
distance=3
value = i - distance
for j in range((int(distance)*2)):
if value < 0 or value > len(ID):
print("Disregard") #Outside acceptable distance range
else:
List1.append(value)
value=value+1
Index_Options.append(List1)
print(Index_Options)
#Index_Options gives the possible index positions for each element. ie "A" can occur in only index positions 0,1,2, "B" can occur in only index positions 0,1,2,3 ect.
I'm just struggling on how to then use this information to create all the output permutations.
Any help would be appreciated
You can use a recursive generator function to build the combinations. Instead of generating all possible permutations from ID and then filtering based on Index_Options, it is much more efficient to produce a cartesian product of ID by directly traversing Index_Options:
ID = ["A","B","C","D","E","F","G","H","I","J"]
def combos(d, c = [], s = []):
if not d:
yield c
else:
for i in filter(lambda x:x not in s and x < len(ID), d[0]):
yield from combos(d[1:], c=c+[ID[i]], s=s+[i])
print(list(combos(Index_Options)))
Output (first ten combinations produced):
[['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'I'], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'I', 'H', 'J'], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'I', 'J', 'H'], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'J', 'H', 'I'], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'J', 'I', 'H'], ['A', 'B', 'C', 'D', 'E', 'F', 'H', 'G', 'I', 'J'], ['A', 'B', 'C', 'D', 'E', 'F', 'H', 'G', 'J', 'I'], ['A', 'B', 'C', 'D', 'E', 'F', 'H', 'I', 'G', 'J'], ['A', 'B', 'C', 'D', 'E', 'F', 'H', 'I', 'J', 'G']]
You can use itertools.permutations to create all the possible permutations and then create new list with a check if all the letters are in the correct position
permutations = [p for p in itertools.permutations(ID, len(ID)) if all(i in Index_Options[ID.index(x)] for i, x in enumerate(p))]

Combination of elements in a list with constraints

I am writing a python code and I need help with a task. I have a list of 8 elements
[A,B,C,D,E,F,G,H]
and I need to find all the combinations of shorter lists (4 elements) in lexicographic order such that two elements are taken from the subset A,C,E,G and the other two from B,D,F,H. I know that there is the library itertools, but I don't know how to combine its functions properly to perform this task
The wording of the question is unclear, but I think this is what you want:
array = ['f','g','d','e','c','b','h','a']
first = sorted(array[::2]) # ['c', 'd', 'f', 'h']
second = sorted(array[1::2]) # ['a', 'b', 'e', 'g']
I think this is what you want.
I need the set of all the new lists with length 4 such that the first two elements are taken from A,C,E,G and the other two are from B,D,F,H and I need them to be in lexicographic order.
We get the possible starting letters and ending letters then combine all possible pairs of each of them into all_lists:
from itertools import combinations
lst = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
starters = lst[::2] # ['A', 'C', 'E', 'G']
enders = lst[1::2] # ['B', 'D', 'F', 'H']
all_lists = []
for a in combinations(starters, 2):
for b in combinations(enders, 2):
all_lists.append(sorted(a + b))
print(all_lists) # Gives [['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'F'], ['A', 'B', 'C', 'H'], ['A', 'C', 'D', 'F'], ['A', 'C', 'D', 'H'], ['A', 'C', 'F', 'H'], ...
print(all_lists == sorted(all_lists)) # False now
(Updated to sort each mini-list.)
Come to think of it you could maybe do the second part with itertools.product.

Find the indices at which any element of one list occurs in another, with duplicates

New to Python, coming from MATLAB. My problem is very similar to this post ( Find the indices at which any element of one list occurs in another ), but with some tweaks that I can't quite manage to incorporate (i.e. managing duplicates and missing values).
Following that example, I have two lists, haystack and needles:
haystack = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K']
needles = ['F', 'G', 'H', 'I', 'F', 'K']
However, both haystack and needles are lists of dates. I need to create a list of indices in haystack for each element of needles in haystack such that:
result = [5, 6, 7, nan, 5, 9]
The two big differences between my problem and the posted example are:
1. I have duplicates in needles (haystack doesn't have any duplicates), which near as I can tell means I can't use set()
2. On rare occasion, an element in needles may not be in haystack, in which case I want to insert a nan (or other placeholder)
So far I've got this (which isn't efficient enough for how large haystack and needles are):
import numpy as np
def find_idx(a,func):
return [i for (i,val) in enumerate(a) if func(val)]
haystack = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K']
needles = ['F', 'G', 'H', 'I', 'F', 'K']
result=[]
for x in needles:
try:
idx = find_idx(haystack, lambda y: y==x)
result.append(idx[0])
except:
result.append(np.nan)
As far as I can tell, that code does what I want, but it's not fast enough. More efficient alternatives?
If your arrays are very large it may be worthwhile to make a dictionary to index the haystack:
haystack = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K']
needles = ['F', 'G', 'H', 'I', 'F', 'K']
hayDict = { K:i for i,K in enumerate(haystack) }
result = [ hayDict.get(N,np.nan) for N in needles]
print(result)
# [5, 6, 7, nan, 5, 9]
How about this?
results=[]
haystack = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K']
needles = ['F', 'G', 'H', 'I', 'F', 'K']
for n in needles:
if n in haystack:
results.append(haystack.index(n))
else:
results.append("NaN")
print (results)
or method2:
haystack = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K']
needles = ['F', 'G', 'H', 'I', 'F', 'K']
results=[]
def getInd(n, haystack):
if n in haystack:
return haystack.index(n)
else:
return "NaN"
for n in needles:
results.append(getInd(n, haystack))
print (results)

Merging a list of lists

How do I merge a list of lists?
[['A', 'B', 'C'], ['D', 'E', 'F'], ['G', 'H', 'I']]
into
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
Even better if I can add a value on the beginning and end of each item before merging the lists, like html tags.
i.e., the end result would be:
['<tr>A</tr>', '<tr>B</tr>', '<tr>C</tr>', '<tr>D</tr>', '<tr>E</tr>', '<tr>F</tr>', '<tr>G</tr>', '<tr>H</tr>', '<tr>I</tr>']
Don't use sum(), it is slow for joining lists.
Instead a nested list comprehension will work:
>>> x = [['A', 'B', 'C'], ['D', 'E', 'F'], ['G', 'H', 'I']]
>>> [elem for sublist in x for elem in sublist]
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
>>> ['<tr>' + elem + '</tr>' for elem in _]
The advice to use itertools.chain was also good.
import itertools
print [('<tr>%s</tr>' % x) for x in itertools.chain.from_iterable(l)]
You can use sum, but I think that is kinda ugly because you have to pass the [] parameter. As Raymond points out, it will also be expensive. So don't use sum.
To concatenate the lists, you can use sum
values = sum([['A', 'B', 'C'], ['D', 'E', 'F'], ['G', 'H', 'I']], [])
To add the HTML tags, you can use a list comprehension.
html_values = ['<tr>' + i + '</tr>' for i in values]
Use itertools.chain:
>>> import itertools
>>> list(itertools.chain(*mylist))
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
Wrapping the elements in HTML can be done afterwards.
>>> ['<tr>' + x + '</tr>' for x in itertools.chain(*mylist)]
['<tr>A</tr>', '<tr>B</tr>', '<tr>C</tr>', '<tr>D</tr>', '<tr>E</tr>', '<tr>F</tr>',
'<tr>G</tr>', '<tr>H</tr>', '<tr>I</tr>']
Note that if you are trying to generate valid HTML you may also need to HTML escape some of the content in your strings.

Categories