Warning: this question is not what you think!
Suppose I have a string like this (Python):
'[[1, 2], [2, 3], [0, 3]]'
Now suppose further that I have the permutation of the characters 0, 1, 2, 3 which swaps 0 and 1, as well as (separately) 2 and 3. Then I would wish to obtain
'[[0, 3], [3, 2], [1, 2]]'
from this. As another example, suppose I want to use the more complicated permutation where 1 goes to 2, 2 goes to 3, and 3 goes to 1? Then I would desire the output
'[[2, 3], [3, 1], [0, 1]]'
Question: Given a permutation (encoded however one likes) of characters/integers 0 to n-1 and a string containing (some of) them, I would like a function which takes such a string and gives the appropriate resulting string where these characters/integers have been permuted - and nothing else.
I have been having a lot of difficult seeing whether there is some obvious use of re or even just indexing that will help me, because usually these replacements are sequential, which would obviously be bad in this case. Any help will be much appreciated, even if it makes me look dumb.
(If someone has an idea for the original list [[1, 2], [2, 3], [0, 3]], that is fine too, but that is a list of lists and presumably more annoying than a string, and the string would suffice for my purposes.)
Here's a simple solution using a regular expression with callback:
import re
s = '[[1, 2], [2, 3], [0, 3]]'
map = [3, 2, 1, 0]
print(re.sub('\d+', # substitute all numbers
lambda m : str(map[int(m.group(0))]), # ... with the mapping
s # ... for string s
)
)
# output: [[2, 1], [1, 0], [3, 0]]
Well I think in general you'll need to use a working memory copy of the resultant to avoid the sequential issue you mention. Also converting to some structured data format like an array to work in makes things much easier (you don't say so but your target string is clearly a stringified array so I'm taking that for granted). Here is one idea using eval and numpy:
import numpy as np
s = '[[2, 3], [3, 1], [0, 1]]'
a = np.array(eval(s))
print('before\n', a)
mymap = [1,2,3,0]
a = np.array([mymap[i] for i in a.flatten()]).reshape(a.shape)
print('after\n', a)
Gives:
before
[[2 3]
[3 1]
[0 1]]
after
[[3 0]
[0 2]
[1 2]]
permutation = {'0':'1', '1':'0', '2':'3', '3':'2'}
s = '[[1, 2], [2, 3], [0, 3]]'
rv = ''
for c in s:
rv += permutation.get(c, c)
print(rv)
?
You can build a mapping of your desired transformations:
import ast
d = ast.literal_eval('[[1, 2], [2, 3], [0, 3]]')
m = {1: 2, 2: 3, 3: 1}
new_d = [[m.get(i) if i in m else
(lambda x:i if not x else x[0])([a for a, b in m.items() if b == i]) for i in b] for b in d]
Output:
[[2, 3], [3, 1], [0, 1]]
For the first desired swap:
m = {0:1, 2:3}
d = ast.literal_eval('[[1, 2], [2, 3], [0, 3]]')
new_d = [[m.get(i) if i in m else
(lambda x:i if not x else x[0])([a for a, b in m.items() if b == i]) for i in b] for b in d]
Output:
[[0, 3], [3, 2], [1, 2]]
This is absolutely inelegant regarding the quality of this forum I confess but here is my suggestion just to help:
string = '[[1, 2], [2, 3], [0, 3]]'
numbers = dict(zero = 0, one = 1, two = 2, three=3, four = 4, five = 5, six=6, seven=7, height=8, nine = 9)
string = string.replace('0', 'one').replace('1', 'zero').replace('2','three').replace('3', 'two')
for x in numbers.keys():
string = string.replace(x, str(numbers[x]))
[[0, 3], [3, 2], [1, 2]]
Related
For a given N, I have a list of all numbers from 0 to N-1
A = list(range(0,N));
and I want to find a list of all possible decompositions into lists of sizes two or higher, without repetitions. For example, for N=4 I have
A = [0,1,2,3];
and the output I want is
OUT = [[[0, 1, 2, 3]], [[0, 1], [2, 3]], [[0, 2], [1, 3]], [[0, 3], [1, 2]]];
Up to N=5, the decomposition of the initial list into only two pieces (of length 2 and 3) makes the problem very easy. However, I can't find a way to do it for higher N, since the lists of length four must be further split into two lists of length 2.
Does anyone have any suggestions on how to solve this? I feel there must be a straightforward recursive trick to do this, but I have been trying for a day now, and I am a little stuck!
Thanks!
PS: the results for N smaller than 6 are:
N=1) OUT = [[[0]]];
N=2) OUT = [[[0, 1]]];
N=3) OUT = [[[0, 1, 2]]];
N=4) OUT = [[[0, 1, 2, 3]], [[0, 1], [2, 3]], [[0, 2], [1, 3]], [[0, 3], [1, 2]]];
N=5) OUT = [[[0, 1, 2, 3, 4]], [[0, 1], [2, 3, 4]], [[0, 2], [1, 3, 4]], [[0, 3], [1, 2, 4]], [[0, 4], [1, 2, 3]], [[1, 2], [0, 3, 4]], [[1, 3], [0, 2, 4]], [[1, 4], [0, 2, 3]], [[2, 3], [0, 1, 4]], [[2, 4], [0, 1, 3]], [[3, 4], [0, 1, 2]]];
PPS: I am a physicist and haven't been programming for a while; the code probably looks terrible, and the problem might be very easy... sorry for that!
Consider the last item i.e N-1, suppose we have two sets of combinations. one for list(range(0,N-1)) and one for list(range(0,N-2)). Now, if you want to put the last item in these combinations, you should have a different approach for each one of them, which I've explained below:
Combinations of list(range(0, N-1)): To put the last item in these combinations, you have no choice other than to put it in one of the sets that are already available to understand this better consider that you have all combinations for four and now you want to add 5 to this combination. So you have :
[[[0, 1, 2, 3]], [[0, 1], [2, 3]], [[0, 2], [1, 3]], [[0, 3], [1, 2]]]
Adding last item( i.e 4) to these combination would get us something like bellow:
[[[0, 1, 2, 3, 4]], [[0, 1, 4], [2, 3]], [[0, 1], [2, 3, 4]], [[0, 2, 4], [1, 3]], [[0, 2], [1, 3, 4]], [[0, 3, 4], [1, 2]], [[0, 3], [1, 2, 4]]]
We put 4 in every combination and make new combinations out of them. So for this part, we need a recursive call for N-1. As you can see in this situation, each sets that N-1 is in has at least three items. That's because we expanded our set, which already existed and had at least two items.
Combinations of N-2 items: I've considered this for when N-1 is in a set with only two items. To find these combinations, we need to select one of the rest N-1 items and consider that selected item with item N-1 as one set and find every other combination for the rest of N-2 items. To give you an example again, consider N = 5 so the last item is 4. If we want to pair the last item with 3, we could build all combinations for (0, 1, 2) and put pair of the (3, 4) to the mix. It would be something like this for N=5:
[[[0 , 1 , 2] , [3 , 4]] , [[0 , 1 , 3] , [2 , 4]] , [[0 , 2 , 3] , [1 , 4]] , [[ 1 , 2 , 3] , [0 , 4]]]
I've implemented this using recursive functions in python. I'm not a python developer, so there may be some enhancements in implementations, but the algorithm is working fine:
import copy
def recursive_builder(arr):
if len(arr) == 3:
return [[[arr[0], arr[1], arr[2]]]]
if len(arr) == 4:
return [[[arr[0], arr[1], arr[2], arr[3]]], [[arr[0], arr[1]], [arr[2], arr[3]]], [[arr[0], arr[2]], [arr[1], arr[3]]], [[arr[0], arr[3]], [arr[1], arr[2]]]]
temp_array = arr[0:len(arr)-1]
recursive_builder_one_step_before = recursive_builder(temp_array)
new_from_one_step_before = []
last_item = arr[len(arr)-1]
for item in recursive_builder_one_step_before:
for i in range(0 , len(item)):
temp_item = copy.deepcopy(item)
temp_item[i].append(last_item)
new_from_one_step_before.append(temp_item)
new_from_two_step_before = []
for i in range(0 , len(temp_array)):
new_arr = temp_array[:i] + temp_array[i+1 :]
recursive_builder_two_step_before = recursive_builder(new_arr)
new_from_two_step_before_inner = []
for item in recursive_builder_two_step_before:
new_item = item + [[temp_array[i] , last_item]]
new_from_two_step_before_inner.append(new_item)
new_from_two_step_before = new_from_two_step_before + new_from_two_step_before_inner
return new_from_two_step_before + new_from_one_step_before
N=6
recursive_builder(list(range(0,N)))
You could run this code on Colab
Edit: I've added memorization to improve the performance a little bit, but my build_from_memory is not O(1), so the improvement could be much better if I could improve the performance of that function.
memotization_dict = {3 : [[[0, 1, 2]]] , 4 : [[[0, 1, 2, 3]], [[0, 1], [2, 3]], [[0, 2], [1, 3]], [[0, 3], [1, 2]]] }
def build_from_memory(arr):
memory = memotization_dict[len(arr)]
ret_val = []
for item in memory:
l2 = []
for i in item:
l1 = []
for j in i:
l1.append(arr[j])
l2.append(l1)
ret_val.append(l2)
return ret_val
def recursive_builder(arr):
if len(arr) in memotization_dict:
return build_from_memory(arr)
temp_array = arr[0:len(arr)-1]
recursive_builder_one_step_before = recursive_builder(temp_array)
new_from_one_step_before = []
last_item = arr[len(arr)-1]
for item in recursive_builder_one_step_before:
for i in range(0 , len(item)):
temp_item = copy.deepcopy(item)
temp_item[i].append(last_item)
new_from_one_step_before.append(temp_item)
new_from_two_step_before = []
for i in range(0 , len(temp_array)):
new_arr = temp_array[:i] + temp_array[i+1 :]
recursive_builder_two_step_before = recursive_builder(new_arr)
new_from_two_step_before_inner = []
for item in recursive_builder_two_step_before:
new_item = item + [[temp_array[i] , last_item]]
new_from_two_step_before_inner.append(new_item)
new_from_two_step_before = new_from_two_step_before + new_from_two_step_before_inner
if(arr == list(range(0 , len(arr)))):
memotization_dict[len(arr)] = new_from_two_step_before + new_from_one_step_before
return new_from_two_step_before + new_from_one_step_before
I'm struggling with something that may be very simple or not possible.
I want to add an numpy array to another numpy array at a specific index.
a = np.zeros(shape=(17, 1, 2))
for i in range(10):
b = [i]
c = [1,2,3,4]
b.append(c)
# Here I want to add b in a at specific index but it's not working
# np.append(a[i][0][0], b)
At the end I want something like that :
a = [[[[0, [1,2,3,4]], ....]]]
Thank you
Your example is not very clear and you do not say what actually is going wrong. You are not doing anything with a in the loop for example. Also you are trying to mix lists and arrays.
Still, I think I know what you mean/need.
You can use insert and append for lists, as illustrated in the example below:
a = []
for i in range(10):
b = [i]
c = [1,2,3,4]
b.insert(1,c)
a.append( b )
print a
Update
Use list.insert(index, obj) to insert an object at a specific index.
If the following is not near to what you'd like, you'll really have to be more specific, dear OP.
I acknowledge numpy to be a powerful library, but you ask it to initialize zeros, which are int then want to add in list. You cannot expect the constructor to know at creation time that it needs to allocate space for object type data. What you want is to help the numpy ndarray constructor with type inference.
a = np.zeros(shape=(17, 1, 2), dtype=object)
for i in range(10):
b = [i]
c = [1,2,3,4]
b.append(c)
a[i] = b
a
#array([[[0, [1, 2, 3, 4]]],
#
# [[1, [1, 2, 3, 4]]],
#
# [[2, [1, 2, 3, 4]]],
#
# [[3, [1, 2, 3, 4]]],
#
# [[4, [1, 2, 3, 4]]],
# [[5, [1, 2, 3, 4]]],
# [[6, [1, 2, 3, 4]]],
# [[7, [1, 2, 3, 4]]],
# [[8, [1, 2, 3, 4]]],
# [[9, [1, 2, 3, 4]]],
# [[0, 0]],
# [[0, 0]],
# [[0, 0]],
# [[0, 0]],
# [[0, 0]],
# [[0, 0]],
# [[0, 0]]], dtype=object)
I have two nested lists:
a = [[1, 1, 1], [1, 1, 1]]
b = [[2, 2, 2], [2, 2, 2]]
I want to make:
c = [[3, 3, 3], [3, 3, 3]]
I have been referencing the zip documentation, and researching other posts, but don't really understand how they work. Any help would be greatly appreciated!
You may use list comprehension with zip() as:
>>> a = [[1, 1, 1], [1, 1, 1]]
>>> b = [[2, 2, 2], [2, 2, 2]]
>>> [[i1+j1 for i1, j1 in zip(i,j)] for i, j in zip(a, b)]
[[3, 3, 3], [3, 3, 3]]
More generic way is to create a function as:
def my_sum(*nested_lists):
return [[sum(items) for items in zip(*zipped_list)] for zipped_list in zip(*nested_lists)]
which can accept any number of list. Sample run:
>>> a = [[1, 1, 1], [1, 1, 1]]
>>> b = [[2, 2, 2], [2, 2, 2]]
>>> c = [[3, 3, 3], [3, 3, 3]]
>>> my_sum(a, b, c)
[[6, 6, 6], [6, 6, 6]]
If you're going to do this a whole bunch, you'll be better off using numpy:
import numpy as np
a = [[1, 1, 1], [1, 1, 1]]
b = [[2, 2, 2], [2, 2, 2]]
aa = np.array(a)
bb = np.array(b)
c = aa + bb
Working with numpy arrays will be much more efficient than repeated uses of zip on lists. On top of that, numpy allows you to work with arrays much more expressively so the resulting code us usually much easier to read.
If you don't want the third party dependency, you'll need to do something a little different:
c = []
for a_sublist, b_sublist in zip(a, b):
c.append([a_sublist_item + b_sublist_item for a_sublist_item, b_sublist_item in zip(a_sublist, b_sublist)])
Hopefully the variable names make it clear enough what it going on here, but basically, each zip takes the inputs and combines them (one element from each input). We need 2 zips here -- the outermost zip pairs lists from a with lists from b whereas the inner zip pairs up individual elements from the sublists that were already paired.
I use python build-in function map() to do this.
If I have simple list a and b, I sum them as this way:
>>> a = [1,1,1]
>>> b = [2,2,2]
>>> map(lambda x, y: x + y, a, b)
[3, 3, 3]
If I have nested list a and b, I sum them as a similar way:
>>> a = [[1, 1, 1], [1, 1, 1]]
>>> b = [[2, 2, 2], [2, 2, 2]]
>>> map(lambda x, y: map(lambda i, j: i + j, x, y), a, b)
[[3, 3, 3], [3, 3, 3]]
I have a list of lists representing a connectivity graph in Python. This list look like a n*2 matrix
example = [[1, 2], [1, 5], [1, 8], [2, 1], [2, 9], [2,5] ]
what I want to do is to find the value of the first elements of the lists where the second element is equal to a user defined value. For instance :
input 1 returns [2] (because [2,1])
input 5 returns [1,2] (because [1,5] and [2,5])
input 7 returns []
in Matlab, I could use
output = example(example(:,1)==input, 2);
but I would like to do this in Python (in the most pythonic and efficient way)
You can use list comprehension as a filter, like this
>>> example = [[1, 2], [1, 5], [1, 8], [2, 1], [2, 9], [2,5]]
>>> n = 5
>>> [first for first, second in example if second == n]
[1, 2]
You can work with the Python functions map and filter very comfortable:
>>> example = [[1, 2], [1, 5], [1, 8], [2, 1], [2, 9], [2,5] ]
>>> n = 5
>>> map(lambda x: x[0], filter(lambda x: n in x, example))
[1,2]
With lambda you can define anonyme functions...
Syntax:
lambda arg0,arg1...: e
arg0,arg1... are your parameters of the fucntion, and e is the expression.
They use lambda functions mostly in functions like map, reduce, filter etc.
exemple = [[1, 2], [1, 5], [1, 8], [2, 1], [2, 9], [2,5] ]
foundElements = []
** input = [...] *** List of Inputs
for item in exemple:
if item[1] in input :
foundElements.append(item[0])
I'm trying to create a pair of functions that, given a list of "starting" numbers, will recursively add to each index position up to a defined maximum value (much in the same way that a odometer works in a car--each counter wheel increasing to 9 before resetting to 1 and carrying over onto the next wheel).
The code looks like this:
number_list = []
def counter(start, i, max_count):
if start[len(start)-1-i] < max_count:
start[len(start)-1-i] += 1
return(start, i, max_count)
else:
for j in range (len(start)):
if start[len(start)-1-i-j] == max_count:
start[len(start)-1-i-j] = 1
else:
start[len(start)-1-i-j] += 1
return(start, i, max_count)
def all_values(fresh_start, i, max_count):
number_list.append(fresh_start)
new_values = counter(fresh_start,i,max_count)
if new_values != None:
all_values(*new_values)
When I run all_values([1,1,1],0,3) and print number_list, though, I get:
[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1],
[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1],
[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1],
[1, 1, 1], [1, 1, 1], [1, 1, 1]]
Which is unfortunate. Doubly so knowing that if I replace the first line of all_values with
print(fresh_start)
I get exactly what I'm after:
[1, 1, 1]
[1, 1, 2]
[1, 1, 3]
[1, 2, 1]
[1, 2, 2]
[1, 2, 3]
[1, 3, 1]
[1, 3, 2]
[1, 3, 3]
[2, 1, 1]
[2, 1, 2]
[2, 1, 3]
[2, 2, 1]
[2, 2, 2]
[2, 2, 3]
[2, 3, 1]
[2, 3, 2]
[2, 3, 3]
[3, 1, 1]
[3, 1, 2]
[3, 1, 3]
[3, 2, 1]
[3, 2, 2]
[3, 2, 3]
[3, 3, 1]
[3, 3, 2]
[3, 3, 3]
I have already tried making a copy of fresh_start (by way of temp = fresh_start) and appending that instead, but with no change in the output.
Can anyone offer any insight as to what I might do to fix my code? Feedback on how the problem could be simplified would be welcome as well.
Thanks a lot!
temp = fresh_start
does not make a copy. Appending doesn't make copies, assignment doesn't make copies, and pretty much anything that doesn't say it makes a copy doesn't make a copy. If you want a copy, slice it:
fresh_start[:]
is a copy.
Try the following in the Python interpreter:
>>> a = [1,1,1]
>>> b = []
>>> b.append(a)
>>> b.append(a)
>>> b.append(a)
>>> b
[[1, 1, 1], [1, 1, 1], [1, 1, 1]]
>>> b[2][2] = 2
>>> b
[[1, 1, 2], [1, 1, 2], [1, 1, 2]]
This is a simplified version of what's happening in your code. But why is it happening?
b.append(a) isn't actually making a copy of a and stuffing it into the array at b. It's making a reference to a. It's like a bookmark in a web browser: when you open a webpage using a bookmark, you expect to see the webpage as it is now, not as it was when you bookmarked it. But that also means that if you have multiple bookmarks to the same page, and that page changes, you'll see the changed version no matter which bookmark you follow.
It's the same story with temp = a, and for that matter, a = [1,1,1]. temp and a are "bookmarks" to a particular array which happens to contain three ones. And b in the example above, is a bookmark to an array... which contains three bookmarks to that same array that contains three ones.
So what you do is create a new array and copy in the elements of the old array. The quickest way to do that is to take an array slice containing the whole array, as user2357112 demonstrated:
>>> a = [1,1,1]
>>> b = []
>>> b.append(a[:])
>>> b.append(a[:])
>>> b.append(a[:])
>>> b
[[1, 1, 1], [1, 1, 1], [1, 1, 1]]
>>> b[2][2] = 2
>>> b
[[1, 1, 1], [1, 1, 1], [1, 1, 2]]
Much better.
When I look at the desired output I can't help but think about using one of the numpy grid data production functions.
import numpy
first_column, second_column, third_column = numpy.mgrid[1:4,1:4,1:4]
numpy.dstack((first_column.flatten(),second_column.flatten(),third_column.flatten()))
Out[23]:
array([[[1, 1, 1],
[1, 1, 2],
[1, 1, 3],
[1, 2, 1],
[1, 2, 2],
[1, 2, 3],
[1, 3, 1],
[1, 3, 2],
[1, 3, 3],
[2, 1, 1],
[2, 1, 2],
[2, 1, 3],
[2, 2, 1],
[2, 2, 2],
[2, 2, 3],
[2, 3, 1],
[2, 3, 2],
[2, 3, 3],
[3, 1, 1],
[3, 1, 2],
[3, 1, 3],
[3, 2, 1],
[3, 2, 2],
[3, 2, 3],
[3, 3, 1],
[3, 3, 2],
[3, 3, 3]]])
Of course, the utility of this particular approach might depend on the variety of input you need to deal with, but I suspect this could be an interesting way to build the data and numpy is pretty fast for this kind of thing. Presumably if your input list has more elements you could have more min:max arguments fed into mgrid[] and then unpack / stack in a similar fashion.
Here is a simplified version of your program, which works. Comments will follow.
number_list = []
def _adjust_counter_value(counter, n, max_count):
"""
We want the counter to go from 1 to max_count, then start over at 1.
This function adds n to the counter and then returns a tuple:
(new_counter_value, carry_to_next_counter)
"""
assert max_count >= 1
assert 1 <= counter <= max_count
# Counter is in closed range: [1, max_count]
# Subtract 1 so expected value is in closed range [0, max_count - 1]
x = counter - 1 + n
carry, x = divmod(x, max_count)
# Add 1 so expected value is in closed range [1, max_count]
counter = x + 1
return (counter, carry)
def increment_counter(start, i, max_count):
last = len(start) - 1 - i
copy = start[:] # make a copy of the start
add = 1 # start by adding 1 to index
for i_cur in range(last, -1, -1):
copy[i_cur], add = _adjust_counter_value(copy[i_cur], add, max_count)
if 0 == add:
return (copy, i, max_count)
else:
# if we have a carry out of the 0th position, we are done with the sequence
return None
def all_values(fresh_start, i, max_count):
number_list.append(fresh_start)
new_values = increment_counter(fresh_start,i,max_count)
if new_values != None:
all_values(*new_values)
all_values([1,1,1],0,3)
import itertools as it
correct = [list(tup) for tup in it.product(range(1,4), range(1,4), range(1,4))]
assert number_list == correct
Since you want the counters to go from 1 through max_count inclusive, it's a little bit tricky to update each counter. Your original solution was to use several if statements, but here I have made a helper function that uses divmod() to compute each new digit. This lets us add any increment to any digit and will find the correct carry out of the digit.
Your original program never changed the value of i so my revised one doesn't either. You could simplify the program further by getting rid of i and just having increment_counter() always go to the last position.
If you run a for loop to the end without calling break or return, the else: case will then run if there is one present. Here I added an else: case to handle a carry out of the 0th place in the list. If there is a carry out of the 0th place, that means we have reached the end of the counter sequence. In this case we return None.
Your original program is kind of tricky. It has two explicit return statements in counter() and an implicit return at the end of the sequence. It does return None to signal that the recursion can stop, but the way it does it is too tricky for my taste. I recommend using an explicit return None as I showed.
Note that Python has a module itertools that includes a way to generate a counter series like this. I used it to check that the result is correct.
I'm sure you are writing this to learn about recursion, but be advised that Python isn't the best language for recursive solutions like this one. Python has a relatively shallow recursion stack, and does not automatically turn tail recursion into an iterative loop, so this could cause a stack overflow inside Python if your recursive calls nest enough times. The best solution in Python would be to use itertools.product() as I did to just directly generate the desired counter sequence.
Since your generated sequence is a list of lists, and itertools.product() produces tuples, I used a list comprehension to convert each tuple into a list, so the end result is a list of lists, and we can simply use the Python == operator to compare them.