Given some matrix, I need to mirror all the rows in the matrix. For example
[[2, 1],
[4, 3]]
would become
[[1, 2],
[3, 4]]
I managed to do it for the (2 x 2)-case. But I'm having trouble mirroring something like this:
[[1, 2, 3, 4],
[1, 2, 3, 4]]
This has to become
[[4, 3, 2, 1],
[4, 3, 2, 1]]
I want to do this with loops/recursion. If I use recursion, I would probably have as basic step that the inner most elements get swapped first, and then from here on we would make the matrix bigger by also including the outer elements and swapping them too. However, I'm having trouble with the recursion step. After having swapped the inner most elements, I want to include the next to inner most elements in the matrix, and swap them too, and then continue like this until we reach the outer elements. How can this be implemented in code? This is what I did so far:
matrix = [[1, 2, 3, 4],
[1, 2, 3, 4]]
def mirror(matrix):
# This corresponds to the basic step. The two inner most elements get swapped.
if len(matrix) == 2:
for i in range(len(matrix)):
for j in range(len(matrix)):
# Store one element in a temporal variable
temp = matrix[i][j]
matrix[i][j] = matrix[i][len(matrix) - 1]
matrix[i][len(matrix)-1] = temp
return matrix
else:
# Recursion step
for i in range(len(matrix)):
for j in range(len(matrix)):
return (matrix + mirror(matrix[(len(matrix) // 2) - 1 : len(matrix)]))
The recursion step is wrong I think. I tried using the slice operator, but I'm not sure how this should be done correctly. Any help with this problem would be appreciated.
A recursive solution is pretty trivial, just recurse across the array reversing each subarray:
arr= [[2, 1],
[4, 3]]
def reve(l):
# if we have recursed across all sub arrays just return empty list
if not l:
return []
# else reverse the first current sublist l[0] and recurse on the remaining sublists
return [l[0][::-1]] + reve(l[1:])
print(reve(arr))
[[1, 2], [3, 4]]
Which can be written concisely as:
def reve(l):
return [l[0][::-1]] + reve(l[1:]) if l else []
If you wanted it inplace:
arr = [[1, 2, 3, 4],
[1, 2, 3, 4]]
def reve(l):
if not l:
return
# call inplace list.reverse on each sublist
l[0].reverse()
return reve(l[1:])
reve(arr)
Output:
[[4, 3, 2, 1], [4, 3, 2, 1]]
And lastly we can achieve what you want inplace with no slicing at all using iter with the special method __length__hint:
def reve(l):
if l.__length_hint__() == 0:
return
sub = next(l)
sub.reverse()
return reve(l)
reve(iter(arr))
print(arr)
Output:
[[4, 3, 2, 1], [4, 3, 2, 1]]
Both functions might use the map function, but you can use a imperative for also. About my recursive approach, the else statement refers to all cases between the ultimate and second elements of the list, they are being concatenated until the first element is reached.
My recursive approach:
a = [[1, 2, 3],
[5, 6, 7]]
def mirror(matrix):
def revert(row):
if len(row) == 1:
return [row[0]]
else:
return [row[-1]] + revert(row[:-1]) # concatenates two lists.
return [revert(row) for row in matrix]
mirror(a)
My declarative approach:
def mirror(matrix):
def revert(row):
return row[::-1] # copies the array in reverse order
return list(map(revert, matrix)) #<-for python3, and just map(...) for python2
mirror(a)
Both functions outputs
[[3, 2, 1], [7, 6, 5]]
Actually, a more Pythonic way of doing that would be using list comprehensions. You can do that simply by:
matrix = [[1, 2, 3, 4],
[1, 2, 3, 4]]
reversed_matrix = (i[::-1] for i in matrix)
reversed_matrix will be a generator expression. You may convert it into a list by replacing "()" with "[]" In the list comprehension.
i[::-1] reverses the array in-place using slice operator
Related
I am stuck on how to solve this problem.
Given a set of lists in a list, if any two sets of lists contain a common element, the two lists would be combined into one.
Suppose I have a set of lists in a list [[0, 1], [3, 6], [3, 9]]. Notice that [3, 6] and [3, 9] have a common element 3, so they are combined into [3, 6, 9], so how to convert this set of lists in a list into [[0,1], [3, 6, 9]]?
This is my current code but I am stuck.
for i in connected:
for j in connected:
a_set = set(i)
b_set = set(j)
if (a_set & b_set):
i.extend(j)
connected.remove(j)
Challenging! This is my approach:
def combine_commons(input: list) -> list:
combine_found = False
for ct_current, item_current in enumerate(input):
# try to find a element that shares item:
combine_into = None
for ct_search, item_search in enumerate(input):
if ct_current==ct_search: continue # can skip if i==j
if any(i in item_search for i in item_current):
# if any elements match, combine them.
combine_into = item_search
combine_found = True
break
if isinstance(combine_into, list):
input[ct_current] = list(set(item_current + combine_into)) # overwrite with new combined
del input[ct_search]
if combine_found: return combine_commons(input)
return input
print(combine_commons([[0, 1], [3, 6], [3, 9]]))
print(combine_commons([[1,2],[2,3],[2,5],[5,1]]))
# >>> [[0, 1], [9, 3, 6]]
# >>> [[1, 2, 3, 5]]
What it basically does is it loops twice through the list of lists. Then, for each i and j it checks if they have something in common. If they do, combine them into one (overwriting element i with the new long list and deleting element j). This then breaks the loop, so my solution was to check all the items again (looking for mergers) in a recursive fashion. Hope this helps!
Sometimes using the right data structures can make all the difference. Your problem seems to require something like an adjacency matrix to resolve. So, here is a quick way to do this using Graphs.
The intuition is as follows -
This is inspired by the approaches mentioned here. Here is the code which is using highly optimized inbuilt networkx functions.
import networkx as nx
def merge_lists(l):
islands = nx.connected_components(nx.from_edgelist(l))
return [list(i) for i in islands]
print(merge_lists([[0,1],[3,6],[3,9]]))
print(merge_lists([[1,2],[2,3],[2,5],[5,1]]))
print(merge_lists([[1,2],[2,3],[9,8],[5,6],[5,7]]))
[[0, 1], [9, 3, 6]] #case 1, [[0,1],[3,6],[3,9]]
[[1, 2, 3, 5]] #case 2, [[1,2],[2,3],[2,5],[5,1]]
[[1, 2, 3], [8, 9], [5, 6, 7]] #case 3, [[1,2],[2,3],[9,8],[5,6],[5,7]]
Any variations in the cases can be easily accommodated by modifying the graph created in the function.
Here is an idea for this particular case, whether it can handle other edge cases is another question but perhaps something you can build on
connected = [[0, 1], [3, 6], [3, 9]]
new_list = []
for i, v in enumerate(connected):
for j in v:
try:
if j in connected[i+1]:
new_list.append(sorted(list(set(connected[i] + connected[i+1]))))
connected.pop(i)
connected.pop(i)
break
except IndexError:
pass
connected += new_list
print(connected)
From this list:
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
I'm trying to create:
L = [[1],[2,2],[3,3,3],[4,4,4,4],[5,5,5,5,5]]
Any value which is found to be the same is grouped into it's own sublist.
Here is my attempt so far, I'm thinking I should use a while loop?
global n
n = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5] #Sorted list
l = [] #Empty list to append values to
def compare(val):
""" This function receives index values
from the n list (n[0] etc) """
global valin
valin = val
global count
count = 0
for i in xrange(len(n)):
if valin == n[count]: # If the input value i.e. n[x] == n[iteration]
temp = valin, n[count]
l.append(temp) #append the values to a new list
count +=1
else:
count +=1
for x in xrange (len(n)):
compare(n[x]) #pass the n[x] to compare function
Use itertools.groupby:
from itertools import groupby
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
print([list(j) for i, j in groupby(N)])
Output:
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
Side note: Prevent from using global variable when you don't need to.
Someone mentions for N=[1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 1] it will get [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5], [1]]
In other words, when numbers of the list isn't in order or it is a mess list, it's not available.
So I have better answer to solve this problem.
from collections import Counter
N = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
C = Counter(N)
print [ [k,]*v for k,v in C.items()]
You can use itertools.groupby along with a list comprehension
>>> l = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
>>> [list(v) for k,v in itertools.groupby(l)]
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
This can be assigned to the variable L as in
L = [list(v) for k,v in itertools.groupby(l)]
You're overcomplicating this.
What you want to do is: for each value, if it's the same as the last value, just append it to the list of last values; otherwise, create a new list. You can translate that English directly to Python:
new_list = []
for value in old_list:
if new_list and new_list[-1][0] == value:
new_list[-1].append(value)
else:
new_list.append([value])
There are even simpler ways to do this if you're willing to get a bit more abstract, e.g., by using the grouping functions in itertools. But this should be easy to understand.
If you really need to do this with a while loop, you can translate any for loop into a while loop like this:
for value in iterable:
do_stuff(value)
iterator = iter(iterable)
while True:
try:
value = next(iterator)
except StopIteration:
break
do_stuff(value)
Or, if you know the iterable is a sequence, you can use a slightly simpler while loop:
index = 0
while index < len(sequence):
value = sequence[index]
do_stuff(value)
index += 1
But both of these make your code less readable, less Pythonic, more complicated, less efficient, easier to get wrong, etc.
You can do that using numpy too:
import numpy as np
N = np.array([1,2,2,3,3,3,4,4,4,4,5,5,5,5,5])
counter = np.arange(1, np.alen(N))
L = np.split(N, counter[N[1:]!=N[:-1]])
The advantage of this method is when you have another list which is related to N and you want to split it in the same way.
Another slightly different solution that doesn't rely on itertools:
#!/usr/bin/env python
def group(items):
"""
groups a sorted list of integers into sublists based on the integer key
"""
if len(items) == 0:
return []
grouped_items = []
prev_item, rest_items = items[0], items[1:]
subgroup = [prev_item]
for item in rest_items:
if item != prev_item:
grouped_items.append(subgroup)
subgroup = []
subgroup.append(item)
prev_item = item
grouped_items.append(subgroup)
return grouped_items
print group([1,2,2,3,3,3,4,4,4,4,5,5,5,5,5])
# [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
So, say I have the following list and variable i:
data = [ [1, 2, 3, 1, 1], [1, 2, 3, 0, 0], [1, 2, 3, 2, 1] ]
i = 3
I would like to create a new list which will sum together and merge numbers from each sublist the ith element upwards to produce the following:
new_data = [ [1, 2, 5], [1, 2, 3], [1, 2, 6] ]
Where I have summed from each sublist the 3rd element upwards and merged the elements together. I would like to ask how, by using the standard library in Python, I can go about achieving this for an arbitrary (integer) value of i. I have the following idea in mind but I'm not sure how to put it into real Python code:
(pseudo-code)
new_data = []
for sublist in data:
new_data.append(elements before the ith element)
new_data.append(ith element onwards summed together)
You can slice your inner list by index and sum the rest with the one-liner:
>>> new_data = [row[:i-1] + [sum(row[i-1:])] for row in data]
>>> new_data
[[1, 2, 5], [1, 2, 5], [1, 2, 5]]
You can find a nice, "pythonic" one-liner in taras' answer:
new_data = [row[:i-1] + [sum(row[i-1:])] for row in data].
From pseudo-code to correct python code
I'll focus my answer on helping you transform your pseudo-code into python code.
Pseudo-code:
new_data = []
for sublist in data:
new_data.append(elements before the ith element)
new_data.append(ith element onwards summed together)
The first issue with your pseudo-code is that you are making a distinction between the list data and its sublists sublist, but you are not making a distinction between the list new_data and its sublists. Let me add a variable new_sublist in the loop:
new_data = []
for sublist in data:
new_sublist = []
new_sublist.append(elements before the ith element)
new_sublist.append(ith element onwards summed together)
new_data.append(new_sublist)
The second issue with your pseudo code: you make two calls to .append in each iteration of the loop. However, these two calls are not similar: the first call is supposed to append several elements, whereas the second call is supposed to append one element. Python makes a distinction between the two operations; if you want to add more than one element at once, use .extend instead of .append. The code becomes:
new_data = []
for sublist in data:
new_sublist = []
new_sublist.extend([elements before the ith element])
new_sublist.append(ith element onwards summed together)
new_data.append(new_sublist)
Finally we can turn your pseudo-code into proper python code, using a list slice to get the elements before the ith element, and builtin function sum along with a list slice to sum the ith element onwards:
new_data = []
for sublist in data:
new_sublist = []
new_sublist.extend(sublist[:i])
new_sublist.append(sum(sublist[i:]))
new_data.append(new_sublist)
Note that in python, looping over a list to construct a new list is such a common operation that we use the compact and elegant list comprehension syntax to do it instead of a multiline for-loop:
new_data = [sublist[:i] + [sum(sublist[i:])] for sublist in data]
Relevant documentation
list.extend and list.append;
list slices;
builtin function sum;
list comprehensions.
You can do it like so:
def merger(data, pos):
pos -= 1 # 0 based, your i above is 1 based
# collection list for result
result = []
# go over each given inner part
for inner in data:
# add it up to the correct position
result.append(inner[:pos])
# extend it by the sum of the remainder values
result[-1].append(sum(inner[pos:]))
return result
data = [ [1, 2, 3, 1, 1], [1, 2, 3, 1, 1], [1, 2, 3, 1, 1] ]
i = 3
print(merger(data,i))
Output:
[[1, 2, 5], [1, 2, 5], [1, 2, 5]]
or in short:
def merger(data, pos):
return [[inner[: pos - 1] + [sum(inner[pos - 1 :])] for inner in data]]
data = [ [1, 2, 3, 1, 1], [1, 2, 3, 1, 1], [1, 2, 3, 1, 1] ]
i = 3
new_data = [sublist[:i-1] + [sum(sublist[i-1:])] for sublist in data]
print(new_data)
>>> [[1, 2, 5], [1, 2, 5], [1, 2, 5]]
Assume no consecutive integers are in the list.
I've tried using NumPy (np.diff) for the difference between each element, but haven't been able to use that to achieve the answer. Two examples of the input (first line) and expected output (second line) are below.
[6, 0, 4, 8, 7, 6]
[[6], [0, 4, 8], [7], [6]]
[1, 4, 1, 2, 4, 3, 5, 4, 0]
[[1, 4], [1, 2, 4], [3, 5], [4], [0]]
You could use itertools.zip_longest to enable iteration over sequential element pairs in your list along with enumerate to keep track of index values where the sequences are not increasing in order to append corresponding slices to your output list.
from itertools import zip_longest
nums = [1, 4, 1, 2, 4, 3, 5, 4, 0]
results = []
start = 0
for i, (a, b) in enumerate(zip_longest(nums, nums[1:])):
if b is None or b <= a:
results.append(nums[start:i+1])
start = i + 1
print(results)
# [[1, 4], [1, 2, 4], [3, 5], [4], [0]]
Here's a simple way to do what you're asking without any extra libraries:
result_list = []
sublist = []
previous_number = None
for current_number in inp:
if previous_number is None or current_number > previous_number:
# still ascending, add to the current sublist
sublist.append(current_number)
else:
# no longer ascending, add the current sublist
result_list.append(sublist)
# start a new sublist
sublist = [current_number]
previous_number = current_number
if sublist:
# add the last sublist, if there's anything there
result_list.append(sublist)
Just cause I feel kind, this will also work with negative numbers.
seq = [6, 0, 4, 8, 7, 6]
seq_by_incr_groups = [] # Will hold the result
incr_seq = [] # Needed to create groups of increasing values.
previous_value = 0 # Needed to assert whether or not it's an increasing value.
for curr_value in seq: # Iterate over the list
if curr_value > previous_value: # It's an increasing value and belongs to the group of increasing values.
incr_seq.append(curr_value)
else: # It was lower, lets append the previous group of increasing values to the result and reset the group so that we can create a new one.
if incr_seq: # It could be that it's empty, in the case that the first number in the input list is a negative.
seq_by_incr_groups.append(incr_seq)
incr_seq = []
incr_seq.append(curr_value)
previous_value = curr_value # Needed so that we in the next iteration can assert that the value is increasing compared to the prior one.
if incr_seq: # Check if we have to add any more increasing number groups.
seq_by_incr_groups.append(incr_seq) # Add them.
print(seq_by_incr_groups)
Below code should help you. However I would recommend that you use proper nomenclature and consider handling corner cases:
li1 = [6, 0, 4, 8, 7, 6]
li2 = [1, 4, 1, 2, 4, 3, 5, 4, 0]
def inc_seq(li1):
lix = []
li_t = []
for i in range(len(li1)):
#print (i)
if i < (len(li1) - 1) and li1[i] >= li1[i + 1]:
li_t.append(li1[i])
lix.append(li_t)
li_t = []
else:
li_t.append(li1[i])
print (lix)
inc_seq(li1)
inc_seq(li2)
You can write a simple script and you don't need numpy as far as I have understood your problem statement. Try the script below. I have tested it using Python 3.6.7 and Python 2.7.15+ on my Ubuntu machine.
def breakIntoList(inp):
if not inp:
return []
sublist = [inp[0]]
output = []
for a in inp[1:]:
if a > sublist[-1]:
sublist.append(a)
else:
output.append(sublist);
sublist = [a]
output.append(sublist)
return output
list = [1, 4, 1, 2, 4, 3, 5, 4, 0]
print(list)
print(breakIntoList(list))
Explanation:
The script first checks if input List passed to it has one or more elements.
It then initialise a sublist (variable name) to hold elements in increasing order. After that, we append input List's first element into our sublist.
We iterate through the input List beginning from it's second element (Index: 1). We keep on checking if the current element in Input List is greater than last element of sublist (by sublist[-1]). If yes, we append the current element to our sublist (at the end). If not, it means we can't hold that current element in sub-List. We append the sublist to output List and clear the sublist (for holding other increasing order sublists) and add the current element to our sublist.
At the end, we append the remaining sublist to the output List.
Here's an alternative using dict, list comprehensions, and zip:
seq = [1, 4, 1, 2, 4, 3, 5, 4, 0]
dict_seq = {i:j for i,j in enumerate(seq)}
# Get the index where numbers start to decrease
idx = [0] # Adding a zero seems counter-intuitive now; we'll see the benefit later.
for k, v in dict_seq.items():
if k>0:
if dict_seq[k]<dict_seq[k-1]:
idx.append(k)
# Using zip, slice and handling the last entry
inc_seq = [seq[i:j] for i, j in zip(idx, idx[1:])] + [seq[idx[-1:]]]
Output
print(inc_seq)
>>> [[1, 4], [1, 2, 4], [3, 5], [4], [0]]
By initiating idx = [0] and creating 2 sublists idx, idx[1:], we can zip these sublists to form [0:2], [2:5], [5:7] and [7:8] with the list comprehension.
>>> print(idx)
>>> [0, 2, 5, 7, 8]
>>> for i, j in zip(idx, idx[1:]):
print('[{}:{}]'.format(i,j))
[0:2]
[2:5]
[5:7]
[7:8] # <-- need to add the last slide [8:]
I have a function that sorts a list of lists by the first list. When I use the function with the variables like so:
sort_lists(IN[0],IN[1],IN[2])
it works perfectly. Although, as I don't know how many lists my input contains, I want to use this as my variable:
sort_lists(IN[idx] for idx in range(len(IN)))
Although this returns a sorting of one list (the superlist). Why is there a difference between these variables, and how can I improve the code?
Here is the function if decisive (here IN[0] is the input with a number of sublists):
def sort_lists(*args):
zipped_list= zip(*sorted(zip(*args)))
return [list(l) for l in zipped_list]
OUT = sort_lists(data_sort[0],data_sort[1],data_sort[2])
I want to use this output:
OUT = sort_lists(data_sort[idx] for idx in range(len(IN[0])))
Two things to understand here:
*args will give you all function parameters as a tuple
IN[idx] for idx in range(len(IN)) is a generator expression
You can see how your inputs are different if you simply add print statement in your function:
def sort_lists(*args):
print(args)
zipped_list= zip(*sorted(zip(*args)))
return [list(l) for l in zipped_list]
Let the input list of lists be: lists = [[2, 1, 3], [1, 3, 4], [5, 4, 2]].
sort_lists(lists[0], lists[1], lists[2])
will print: ([2, 1, 3], [1, 3, 4], [5, 4, 2]). That's a tuple of inner lists.
Though, if you call it like this:
sort_lists(lists[idx] for idx in range(len(lists)))
or
sort_lists(sublist for sublist in lists)
this will print (<generator object <genexpr> at 0x0000007001D3FBA0>,), a one-element tuple of a generator.
You can make your function work with a generator by accepting only one parameter:
def sort_lists(arg):
zipped_list= zip(*sorted(zip(*arg)))
return [list(l) for l in zipped_list]
sort_lists(lists[idx] for idx in range(len(lists)))
# [[1, 2, 3], [3, 1, 4], [4, 5, 2]]
but I suggest to leave your function as is, and unpack your lists in the place where you call it instead:
>>> sort_lists(*lists)
[[1, 2, 3], [3, 1, 4], [4, 5, 2]]
Just change the function to accept list of lists, is it problem? This piece of code:
IN[idx] for idx in range(len(IN))
returns again list of lists
As Georgy pointed out, the difference is between the arguments being a generator or list. I would also like to point out that this is an opportunity to use and practice the map method. map applies the same function to each entry in a list. The function can be a built-in like sorted.
list_a = [[2, 1, 3], [1, 3, 4], [5, 4, 2]]
sorted_list_a = list(map(sorted, list_a)) # sorted is the python built-in function
print(sorted_list_a)
Returns:
[[1, 2, 3], [1, 3, 4], [2, 4, 5]]
You'll notice that you'll have to pass your map to the list function because map returns a map object, so you have to turn it into a list.
The map documentation is here. And a good example of it is here.