Python how to revert the pattern of a list rearrangement - python

So I am rearranging a list based on an index pattern and would like to find a way to calculate the pattern I need to revert the list back to its original order.
for my example I am using a list of 5 items as I can work out the pattern needed to revert the list back to its original state.
However this isn't so easy when dealing with 100's of list items.
def rearrange(pattern: list, L: list):
new_list = []
for i in pattern:
new_list.append(L[i-1])
return new_list
print(rearrange([2,5,1,3,4], ['q','t','g','x','r']))
#['t', 'r', 'q', 'g', 'x']
and in order to set it back to the original pattern
I would use
print(rearrange([3,1,4,5,2],['t', 'r', 'q', 'g', 'x']))
#['q', 't', 'g', 'x', 'r']
What I am looking for is a way to calculate the pattern "[3,1,4,5,2]"
regarding the above example.
whist running the script so that I can set the list back to its original order.
Using a larger example:
print(rearrange([18,20,10,11,13,1,9,12,16,6,15,5,3,7,17,2,19,8,14,4],['e','p','b','i','s','r','q','h','m','f','c','g','d','k','l','t','a','n','j','o']))
#['n', 'o', 'f', 'c', 'd', 'e', 'm', 'g', 't', 'r', 'l', 's', 'b', 'q', 'a', 'p', 'j', 'h', 'k', 'i']
but I need to know the pattern to use with this new list in order to return it to its original state.
print(rearrange([???],['n', 'o', 'f', 'c', 'd', 'e', 'm', 'g', 't', 'r', 'l', 's', 'b', 'q', 'a', 'p', 'j', 'h', 'k', 'i']))
#['e','p','b','i','s','r','q','h','m','f','c','g','d','k','l','t','a','n','j','o']

This is commonly called "argsort". But since you're using 1-based indexing, you're off-by-one. You can get it with numpy:
>>> pattern
[2, 5, 1, 3, 4]
>>> import numpy as np
>>> np.argsort(pattern) + 1
array([3, 1, 4, 5, 2])
Without numpy:
>>> [1 + i for i in sorted(range(len(pattern)), key=pattern.__getitem__)]
[3, 1, 4, 5, 2]

What about something like below:
def revert_pattern(pattern):
pattern_i = [0]*len(pattern)
for k in range(len(pattern)):
pattern_i[pattern[k]-1] = k+1
return pattern_i
print(revert_pattern([2, 5, 1, 3, 4]))
# [3, 1, 4, 5, 2]
Note: I followed your logic but I recommend you using 0 as the smallest indexes instead of 1 since it requires somes extra +1/-1 that could be avoided

def rearrange(p, l):
arr = [l[i - 1] for i in p]
d = {v : i + 1 for i, v in enumerate(arr)}
order = [d[k] for k in l]
return arr, order
a = [2, 5, 1, 3, 4]
b = ['q', 't', 'g', 'x', 'r']
rearrange(a, b)
# (['t', 'r', 'q', 'g', 'x'], [3, 1, 4, 5, 2])
OR maybe
def revert(p):
z = zip(p, list(range(len(p))))
return [x + 1 for _, x in sorted(z)]
a = [2, 5, 1, 3, 4]
revert(a)
# [3, 1, 4, 5, 2]

Related

Extract patterns from tuples

I have a list of tuples
a=[('a', 0), ('c', 1), ('d', 0), ('b', 1), ('t',1), ('j',2), ('k',3), ('s', 4), ('l',1), ('y',1), ('r',2), ('b',3), ('k',4)]
I want output like
[[1,1,1,2,3,4],[1,1,2,3,4]]
and corresponding letters
[['c', 'b', 't', 'j', 'k', 's'], ['l', 'y', 'r', 'b', 'k']]
I need to remove 0's in between and the pattern always starts with 1
Using a simple loop and tracking the previous non-zero value:
letters = []
numbers = []
prev = 2
for l,n in a:
if n == 0:
continue
elif prev > 1 and n == 1:
letters.append([])
numbers.append([])
letters[-1].append(l)
numbers[-1].append(n)
prev = n
letters
# [['c', 'b', 't', 'j', 'k', 's'], ['l', 'y', 'r', 'b', 'k']]
numbers
# [[1, 1, 1, 2, 3, 4], [1, 1, 2, 3, 4]]

Split a list into various slices and concatenate it

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]

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.

Python: how to replicate the same row of a matrix?

How can I copy each row of an array n times?
So if I have a 2x3 array, and I copy each row 3 times, I will have a 6x3 array. For example, I need to convert A to B below:
A = np.array([[1, 2, 3],
[4, 5, 6]])
B = np.array([[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
[4, 5, 6],
[4, 5, 6],
[4, 5, 6]])
If possible, I would like to avoid a for loop.
If I read correctly, this is probably what you want assuming you started with mat:
transformed = np.concatenate([np.vstack([mat[:, i]] * 3).T for i in range(mat.shape[1])], axis=1)
Here's a verifiable example:
# mocking a starting array
import string
mat = np.random.choice(list(string.ascii_lowercase), size=(5,3))
>>> mat
array([['s', 'r', 'e'],
['g', 'v', 'c'],
['i', 'b', 'd'],
['f', 'g', 's'],
['o', 'm', 'w']], dtype='<U1')
Transform it:
# this repeats it 3 times for sake of displaying
transformed = np.concatenate([np.vstack([mat[i, :]] * 3).T for i in range(mat.shape[0])], axis=1).T
>>> transformed
array([['s', 'r', 'e'],
['s', 'r', 'e'],
['s', 'r', 'e'],
['g', 'v', 'c'],
['g', 'v', 'c'],
['g', 'v', 'c'],
['i', 'b', 'd'],
['i', 'b', 'd'],
['i', 'b', 'd'],
['f', 'g', 's'],
['f', 'g', 's'],
['f', 'g', 's'],
['o', 'm', 'w'],
['o', 'm', 'w'],
['o', 'm', 'w']], dtype='<U1')
The idea of this is to use vstack to concatenate each column to itself multiple time, and then concatenate the result of that to get the final array.
You can use np.repeat with integer positional indexing:
B = A[np.repeat(np.arange(A.shape[0]), 3)]
array([[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
[4, 5, 6],
[4, 5, 6],
[4, 5, 6]])
v1=[3,2]
v3=v1[:]*10
print(v3)
np.repeat is exactly what you are looking for. You can use the axis option to specify that you want to duplicate rows.
B = np.repeat(A, 3, axis=0)

fPython : Making a new list from a random list of letters

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']#alphabet
bag_o_letters = []#letters to chose from
letter_count = [9, 2, 2, 4, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, 1]#random indexes to chose from
for x in range(26):#loops through the random index of letter_count
for i in range(letter_count[x]):#chooses the index
bag_o_letters.append(letters[x])#appends the index of the letter to bag_o_letters
rack = []#list for the person to see
for a in range(7):#makes the list 7 letters long
rack.append(bag_o_letters.pop(random.randint(0,len(letters)-1)))#appends the letter to rack(supposedly...)
print(rack)
In this code that you just read it should choose random letters and put 7 of those letters in a rack that the person can see. It shows a error that I've looked over many times, but I just can't see what is wrong.
I put comments on the side to understand the code.
It shows this error:
rack.append(bag_of_letters.pop(random.randint(0,len(letters)-1)))
IndexError: pop index out of range
Can someone please help?
After this code, I am going to make a input statement for the user to make a word from those letters.
The first time through the loop, you append one value to bag_of_letters, and then you try to pop an index of random.randint(0,len(letters)-1). It doesn't have that many elements to pop from yet. Instead of this approach, you can make a list of the required length and sample from it:
letters = ['a', ...]#alphabet
letter_count = [9, ...]#random indexes to chose from
bag_of_letters = [l*c for l,c in zip(letters, letter_count)]
...
rack = random.sample(bag_o_letters, 7)
You're selecting the index to pop for bag_of_letters from the length of letters which is obviously larger.
You should instead do:
rack.append(bag_of_letters.pop(random.randint(0, len(bag_of_letters)-1)))
# ^^^^^^^^^^^^^^
However, there are likely to be more problems with your code. I'll suggest you use random.sample in one line of code or random.shuffle on a copy of the list, and then slice up till index 7. Both will give you 7 randomly selected letters:
import random
print(random.sample(letters, 7))
# ['m', 'u', 'l', 'z', 'r', 'd', 'x']
import random
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']
letters_copy = letters[:]
random.shuffle(letters_copy)
print(letters_copy[:7])
# ['c', 'e', 'x', 'b', 'w', 'f', 'v']
The IndexError is expected:
pop(...)
L.pop([index]) -> item -- remove and return item at index (default last).
Raises IndexError if list is empty or index is out of range.
You need to subtract 1 from the bounds of the call to random() after each pop(). Right now you are doing this:
l = [1,2,3]
random_idx = 2
l.pop(random_idx)
>>> l == [1,3]
random_idx = 3
l.pop(random_idx)
>>>> IndexError: pop index out of range
So instead, pop() based on len(bag_o_letter) rather than len(letter).
Why not do something like this:
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']
letter_count = [9, 2, 2, 4, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, 1]#random indexes to chose from
from random import shuffle
all_letters = list(''.join([l*c for l,c in zip(letters, letter_count)]))
shuffle(all_letters)
for i in range(int(len(all_letters)/7)):
print all_letters[i*7:(i+1)*7]
So I assume this is for something like scrabble? Your issue is that you're choosing a random index from your list of letters, not bag_o_letters. Maybe try this:
rack = []
for i in range(7):
index = random.randint(0, len(bag_o_letter) - 1)
rack.append(bag_o_letters.pop(index))

Categories

Resources