Combine lists within a list based on conditionals - python

I am trying to create a for loop to combine lists within a list based on criteria.
My dataset has 300 records of pets. There are 3 types of animals (ex. dog, cat, bird), an interval for the year each animal was born (ex. (1990, 2000], (2000, 2004], etc.) and an interval for a friendly meter rating for each animal (ex. (0,2], (2, 4], (4, 6], etc.)
An example image of what the dataset may look like can be found here:
https://i.stack.imgur.com/YwbGN.png
The current code I am using to combine the friendly meter ratings is below:
friendly_meter_combos = []
combo = []
for val in df['friendly_meter'].unique():
if len(combo) < 2:
combo.append(val)
else:
friendly_meter_combos.append(combo)
combo = []
combo.append(val)
This provides me with an example outcome for
friendly_meter_combos =
[[(0, 2], (2, 4]],
[(4, 6], (6, 8]],
[(8, 10], (2, 4]],
[(4, 6], (6, 8]],
...].
As you can see in the second to last line I printed, the numbers start over ([(8, 10], (2, 4]]). This is because I'm now on a different interval for the year born for the same animal type in the dataset. I would like to figure out how to start the code over to get the output below instead
friendly_meter_combos =
[[(0, 2], (2, 4]],
[(4, 6], (6, 8]],
[(8, 10]],
[(2, 4], (4, 6]],
...].
Any help would be greatly appreciated! Thanks!

As much as I understand, you are looking to have a combination of consecutive "friendly meters"
# let's suppose unique_values is a list containing ordered friendly meters
unique_values = [ [0, 2], [2, 4], [4, 6], [6, 8], [8, 10] ]
# n is the size of the combo
n = 2
# use statement below
friendly_meter_combos = list(zip(*(unique_values[i:] for i in range(n))))
Result is:
[([0, 2], [2, 4]), ([2, 4], [4, 6]), ([4, 6], [6, 8]), ([6, 8], [8, 10])]

Related

I like to eliminate duplicate components from double list, and then combine the double lists

I need your help to figure out this problem.
when I use this code, I can get this result.
input=[[1],[1,2],[5,7]]
output=[[1,2],[5,7]]
Tb2 = Tb[:1]
for t in Tb[1:]:
if set(t).isdisjoint(Tb2[-1]):
Tb2.append(t)
else:
Tb2[-1] = sorted({*t,*Tb2[-1]})
But I can't solve the problem when another second list have same number in input.
input=[[2,3],[1,2],[5,7],[5,8],[7,8,9],[1]]
expected output=[[1,2,3],[5,7,8,9]]
Would you give me advice or help?
This seems like connected components problem from graph theory, so you could use networkx for it:
import networkx as nx
from itertools import combinations
# input
lst = [[2, 3], [1, 2], [5, 7], [5, 8], [7, 8, 9], [1]]
# build graph
g = nx.Graph()
g.add_edges_from([edge for ls in lst for edge in combinations(ls, 2)])
# compute components
components = nx.algorithms.components.connected_components(g)
res = list(components)
print(res)
Output
[{1, 2, 3}, {8, 9, 5, 7}]
The idea is to build an edge between each pair of elements of the same list, this is achieved with the following list comprehension:
[edge for ls in lst for edge in combinations(ls, 2)]
# [(2, 3), (1, 2), (5, 7), (5, 8), (7, 8), (7, 9), (8, 9)]
Once that is done simply run the connected_components algorithm on the graph.
for t in Tb:
for j in range(len(Tb2)):
if set(t).isdisjoint(Tb2[j]):
Tb2.append(t)
else:
Tb2[j] = sorted({*t,*Tb2[j]})
Output= [[1, 2, 3], [5, 7, 8, 9], [5, 7, 8, 9], [7, 8, 9], [1], [1], [1]]
I can't eliminate same and small size of double lists.

Extract columns from list of tables in python

I have a list of tables, where each table is a list of lists. I'd like to extract the columns from each table to get a lists of columns. An example should clarify:
input=[[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]],[[13,14,15],[16,17,18]]]
output=[[[1,4],[7,10],[13,16]],[[2,5],[8,11],[14,17]],[[3,6],[9,12],[15,18]]]
I know I can probably use several for loops, but it feels like there should be a nice one liner to do this.
I intend to assign the results to variables:
a,b,_=output
With a single table, I can extract the columns with:
>>>input=[[1,2,3],[4,5,6]]
>>>list(zip(*input))
[(1, 4), (2, 5), (3, 6)]
or for assignment:
>>>a,b,_=zip(*input)
>>>a
(1, 4)
>>>b
(2, 5)
But for a list of tables, I haven't been able to do it. Here are some of the things I've tried unsuccessfully:
>>>list(zip(*zip(*input)))
[([1, 2, 3], [4, 5, 6]), ([7, 8, 9], [10, 11, 12]), ([13, 14, 15], [16, 17, 18])]
>>>[list(zip(*inp)) for inp in input]
[[(1, 4), (2, 5), (3, 6)], [(7, 10), (8, 11), (9, 12)], [(13, 16), (14, 17), (15, 18)]]
>>>[[x,y,z] for x,y,z in [zip(*inp) for inp in input]]
#same as above
>>> [[x,y] for inp in input for x,y in zip(*inp)]
[[1, 4], [2, 5], [3, 6], [7, 10], [8, 11], [9, 12], [13, 16], [14, 17], [15, 18]]
And nested/unpacked assignment didn't work for me either:
>>>[[a,b,_]]=[zip(*inp) for inp in input]
ValueError: too many values to unpack (expected 1)
>>>[*a,*b,*_]=[[x,y] for x,y in [zip(*inp) for inp in input]]
SyntaxError: two starred expressions in assignment
Is there a one-liner to do what I'm trying to do?
Edit: Note that while the example is specifically 3 tables with 2 rows and 3 columns each, my actual use case has unknown numbers of tables and rows.
I ended up using this line in my code:
list(zip(*[zip(*inp) for inp in input]))
You got close with that last attempt. You need one more, well-chosen nesting level. EDIT: I added the final zip step to get the desired ordering. I also used that "star" notation to help show how to extend the concept.
given = [[[1,2,3],[4,5,6]],
[[7,8,9],[10,11,12]],
[[13,14,15],[16,17,18]]]
f1 = [[[a, b] for a, b in zip(list1, list2)] for list1, list2 in given]
print(f1)
f2 = list(zip(*f1))
print(f2)
Output (edited for readability)
[[[1, 4], [2, 5], [3, 6]],
[[7, 10], [8, 11], [9, 12]],
[[13, 16], [14, 17], [15, 18]]]
[([1, 4], [7, 10], [13, 16]),
([2, 5], [8, 11], [14, 17]),
([3, 6], [9, 12], [15, 18])]
The second one has tuples instead of lists at the middle level; is that okay? If not, can you fix it? (left as an exercise for the student ... )

Calculating array combinations using recursion

I wish to input an n*m array and the resulting output be an array containing the different combinations of the row elements.
Here's an example to clarify (albeit an extremely simple case):
I wish to input an array of the following shape:
[[1, 2, 3]
[2, 5, 6]]
And wish to receive the following output:
[[1,2], [1,5], [1,6], [2,5], [2,6], [3,2], [3,5], [3,6]]
As you can see [2,2] is not included because of repetition.
I can write quick and dirty code containing nested for loops when the input dimensions are know a priori:
A = [[1, 2, 3], [2, 5, 6]]
m = len(A[0])
for i in range(0, m):
for j in range(0, m):
if A[0][i]!=A[1][j]: #check and eliminate repetition
combined.append([A[0][i],A[1][j])
choice_num.append([i+1, j+1]) #See (**) below
I would really like to know how to implement this as a recursive function so given some input n-D array, A, one can simply call it as:
recursive_looper(A)
(**) Another feature that I would like is for the function to output the column number corresponding to the element used in the combination so we get two outputs:
element values: [[1,2], [1,5], [1,6], [2,5], [2,6], [3,2], [3,5], [3,6]]
element position: [[1,1], [1,2], [1,3], [2,2], [2,3], [3,1], [3,2], [3,3]]
Any tips or suggestions would be greatly appreciated!!
Edit: I am open to any solution that can achieve the desired output. Recursion was simply the first thing that came to mind.
Edit 2 (Extended capabilities): This code must not be restricted to a specific list input shape but be extensible to any array of shape (n,m).
I'll provide an example for where the code breaks down. The work-around was implementing n-1 conditional statements, which I would like to avoid because the array shape must be known a priori.
A = [[2, 4, 1, 11, 3], [3, 2, 1, 4, 11], [2, 3, 4, 17, 13]]
If I do not make any modifications to your indexing/filter I receive the following output for the 'filtered' list:
#[[2, 3, 2], [2, 3, 3], [2, 3, 4], [2, 3, 17], [2, 3, 13], [2, 1, 2], ..., [3, 11, 13]]
Immediately I notice that it only compared element position 0 with position 1 for 'likeness', hence why the first combination contains two 2's.
I can make a modification to the Index grabber and filter loop which looks like so:
for i in range(0, len(projects_master)-2):
indexes = [idx for idx, t in enumerate(prod) if t[i] == t[i+1] or t[i]==t[i+2] or t[i+1] == t[i+2] ]
res = []
for i in range(0, len(A)-2):
res.append(list(filter( lambda v: v[i] != v[i+1] and v[i] != v[i+2] and v[i+1] != v[i+2], prod)))
result = [list(t) for t in res[0]]
This does give the correct output, but like I said, I needed to write out n-1 t[i] and v[i] conditions. How can this be done automatically?
EDIT 3 - FINAL
Thanks a bunch to those who provided different approaches to help me achieve the same end goal. I took some insight from each and wrote something that makes sense to me and seems to function well for any input. The code which filters duplicates and removes them from the combinations is shown below:
ind_remove = []
for i in range(0, len(prod)):
if len(prod[i]) != len(set(prod[i])):
ind_remove.append(i)
adder=0
for i in ind_remove:
del prod[i-adder]
adder=adder+1 #takes into account change in indices after an element is deleted.
You can use itertools.product to generate the required combinations, which works like a cartesion product between two sets to generate the combinations.
So it the lists have been [[1, 2], [3, 4]], the cartesian product within the sublists will be
[[1, 3], [1, 4], [2, 3], [2, 4]]
from itertools import product
a = [[1, 2, 3], [2, 5, 6]]
# Generate all possible products, *a gives you two lists
prod = list(product(*a))
#[(1, 2), (1, 5), (1, 6), (2, 2), (2, 5), (2, 6), (3, 2), (3, 5), (3, 6)]
#Get the list of duplicate indexes
indexes = [idx for idx, t in enumerate(prod) if t[0] == t[1] ]
print(indexes)
#[3]
#Remove tuples who are duplicates
res = list(filter( lambda v: v[0] != v[1], prod))
print(res)
#[(1, 2), (1, 5), (1, 6), (2, 5), (2, 6), (3, 2), (3, 5), (3, 6)]
#Convert the tuples to list
result = [list(t) for t in res]
print(result)
#[[1, 2], [1, 5], [1, 6], [2, 5], [2, 6], [3, 2], [3, 5], [3, 6]]
You can use a function that iterates over the items of the first list of the given list of lists and merge each item with the combinations from the recursive calls:
def nonrepetitive_product(lists):
if not lists:
yield []
return
first, *rest = lists
combinations = list(nonrepetitive_product(rest))
for item in first:
for combination in combinations:
if item not in combination:
yield [item, *combination]
so that given:
l = [[1, 2, 3], [2, 5, 6]]
list(nonrepetitive_product(l)) returns:
[[1, 2], [1, 5], [1, 6], [2, 5], [2, 6], [3, 2], [3, 5], [3, 6]]
If you want the positions and values for any number of rows, you'd be better off using itertools.product and enumerate together. Filtering is a little tricky, but it can be done:
import itertools
A = [[1, 2, 3], [2, 5, 6], [7, 8, 3]]
prod = itertools.product(*map(enumerate, A)) # yields ((i,x),(j,y),(k,z),...) nested tuples
transposed = ([*zip(*pairs)] for pairs in prod) # yields ((i,j,k,...), (x,y,z,...)) 2-tuples
filtered = [(ijk, xyz) for ijk, xyz in transposed if len(xyz) == len(set(xyz))] # filter dupes
indexes, values = zip(*filtered) # you might find `filtered` more useful than separate lists

Python Mapping Arrays

I have one array pat=[1,2,3,4,5,6,7] and a second array count=[5,6,7,8,9,10,11]. Is there a way without using dictionaries to get the following array newarray=[[1,5],[2,6],[3,7],[4,8],[5,9],[6,10],[7,11]]?
You can just zip the lists
>>> pat=[1,2,3,4,5,6,7]
>>> count=[5,6,7,8,9,10,11]
>>> list(zip(pat,count))
[(1, 5), (2, 6), (3, 7), (4, 8), (5, 9), (6, 10), (7, 11)]
Or if you want lists instead of tuples
>>> [[i,j] for i,j in zip(pat,count)]
[[1, 5], [2, 6], [3, 7], [4, 8], [5, 9], [6, 10], [7, 11]]
If you want inner elements to be list, you can use -
>>> pat=[1,2,3,4,5,6,7]
>>> count=[5,6,7,8,9,10,11]
>>> newarray = list(map(list,zip(pat,count)))
>>> newarray
[[1, 5], [2, 6], [3, 7], [4, 8], [5, 9], [6, 10], [7, 11]]
This first zips the two lists, combining the ith element of each list, then converts them into lists using map function, and later converts the complete outer map object (that we get from map function) into list
Without using zip, you can do the following:
def map_lists(l1, l2):
merged_list = []
for i in range(len(l1)):
merged_list.append([l1[i], l2[i]])
return merged_list
Or, the equivalent, using a list comprehension instead:
def map_lists(l1, l2):
return [[l1[i], l2[i]] for i in range(len(l1))]

How do I create a recursion of multiple for loops of lists in Python to get combination? [duplicate]

This question already has answers here:
How to get the cartesian product of multiple lists
(17 answers)
Closed 9 years ago.
I am currently using python to execute my code. My input into this function is a list within a list like [ [1,2,3],[4,5],[6,7,8,9] ] The goal is to iterate and create all the possible combinations for each list.
The only way I have it in my mind is to do multiple for loops on each sublist within the master list. Each combination must have at least one element from each sublist so since there are 3 sublists, all combinations must have 3 elements.
for a in [1,2,3]:
for b in [4,5]:
for c in [6,7,8,9]:
l.append([a,b,c])
So one combination would be [1,4,6],[1,4,7],[1,4,8],[1,4,9]. And the next loop with be [1,5,6]..[1,5,7]...and so forth.
This works but my problem is that I don't know how many sublists will be in the master list (input) so I cant just keep writing for loops indefinitely. I know there must be a way to write a recursive function but I have never done this and don't know how it works. I don't think it should be too difficult but can someone show me the algorithm to accomplish this please?
Thank you so much!!
If you are trying to learn about recursion, think this way: The combinations of all the lists consist of taking one item at a time from the first list and prepending this item to all the combinations of the remaining lists.
This way you get
def combinations(rest, prefix = [], result = []):
if rest:
first = rest.pop()
for item in first:
prefix.append(item)
combinations(rest, prefix, result)
prefix.pop()
rest.append(first)
else:
result.append(list(reversed(prefix)))
return result
NB I am far from a Python expert. It's likely there is a more elegant way to code this. Note that because I'm pushing and popping at the ends of lists I need to reverse the final results. The advantage of this is that it's fast and should produce no garbage.
In Idle:
>>> combinations([ [1,2,3],[4,5],[6,7,8,9] ] )
[[1, 4, 6], [2, 4, 6], [3, 4, 6], [1, 5, 6], [2, 5, 6], [3, 5, 6],
[1, 4, 7], [2, 4, 7], [3, 4, 7], [1, 5, 7], [2, 5, 7], [3, 5, 7],
[1, 4, 8], [2, 4, 8], [3, 4, 8], [1, 5, 8], [2, 5, 8], [3, 5, 8],
[1, 4, 9], [2, 4, 9], [3, 4, 9], [1, 5, 9], [2, 5, 9], [3, 5, 9]]
Try using the itertools library:
import itertools
arr = [[1,2], [3,4], [5,6]]
list(itertools.product(*arr))
# => [(1, 3, 5), (1, 3, 6), (1, 4, 5), (1, 4, 6), (2, 3, 5), (2, 3, 6), (2, 4, 5), (2, 4, 6)]

Categories

Resources