Find sequences in list of ranges - python

I have a random length list that contains ranges info:
list = [
[[7, 12], [6, 12], [38, 44], [25, 30], [25, 29]],
[[0, 5], [1, 5], [2, 5], [12, 16], [13, 16], [20, 23], [29, 33], [30, 33]],
[[5, 7], [6, 8], [7, 9], [8, 10], [9, 11], [10, 12], [16, 18], [17, 19], [18, 20], [23, 25], [24, 26], [25, 27], [26, 28], [27, 29], [33, 35], [34, 36], [35, 37], [36, 38], [37, 39], [38, 40], [39, 41], [40, 42], [41, 43], [42, 44]]
]
For example, first element [[7, 12], [6, 12], [38, 44], [25, 30]] contains 4 ranges 7-12, 6-12, 38-44 and 25-30 etc.
I need to find the all possible chains (a chain is an array of consecutive ranges where ending of first range == beginning of next range) of length given list length, given that I could and should take only one range from each row in the exact order of rows.
So, for this example list:
The chains would be
[[6, 12], [12, 16], [16, 18]],
[[7, 12], [12, 16], [16, 18]],
[[25, 30], [30, 33], [33, 35]]
and [[25, 29], [29, 33], [33, 35]]
Right now I am stuck on working with more than three length list, could not come up with recursive solution.

You can use itertools.product to iterate over all possible chains (all combinations of 1 range from each "row"),
then filter them by a simple function that checks if a specific chain is legal.
try this:
from itertools import product
def check_chain(chain):
prev_end = chain[0][1]
for start, end in chain[1:]:
if start != prev_end:
return False
prev_end = end
return True
all_candidate_chains = product(*list)
result = [[*chain] for chain in all_candidate_chains if check_chain(chain)]
print(result)
Output:
[[[7, 12], [12, 16], [16, 18]], [[6, 12], [12, 16], [16, 18]], [[25, 30], [30, 33], [33, 35]]]
EDIT:
can also use zip and all to replace check_chain with a 1-liner:
from itertools import product
result = [[*chain] for chain in product(*list) if all(end1 == start2 for (_, end1), (start2, _) in zip(chain, chain[1:]))]
print(result)

You can do this without looking at all the permutation. Start at with the last item and make a dictionary where the keys are the first value in the dictionary. Then work backward through the list and lookup the previous key based on the second value of the tuple adding to the array as you go:
In the end you'll have a dictionary keyed to the first value in the tuples of the first list. You can just flatten the values at this point.
Here I added one more pair [12,9] to the middle list so I could see it work with branching paths:
from collections import defaultdict
from itertools import chain
l = [
[[7, 12], [6, 12], [38, 44], [25, 30]],
[[0, 5], [1, 5], [2, 5], [12, 16], [12, 9],[13, 16], [20, 23], [29, 33], [30, 33]],
[[5, 7], [6, 8], [7, 9], [8, 10], [9, 11], [10, 12], [16, 18], [17, 19], [18, 20], [23, 25], [24, 26], [25, 27], [26, 28], [27, 29], [33, 35], [34, 36], [35, 37], [36, 38], [37, 39], [38, 40], [39, 41], [40, 42], [41, 43], [42, 44]]
]
d = defaultdict(list)
for k, v in l[-1]:
d[k].append([[k,v]])
for sub in reversed(l[:-1]):
ds = defaultdict(list)
for k, v in sub:
if v in d:
ds[k].extend([[k,v], *v2] for v2 in d[v] )
d = ds
list(chain.from_iterable(d.values()))
Output:
[[[7, 12], [12, 16], [16, 18]],
[[7, 12], [12, 9], [9, 11]],
[[6, 12], [12, 16], [16, 18]],
[[6, 12], [12, 9], [9, 11]],
[[25, 30], [30, 33], [33, 35]]]

Related

Numpy - Reshape 2d array and keep order

I have an array of 64 numbers arranged in an 8x8 using x = np.arange(64).reshape(8, 8) which i would like to reshape to be a 4x4 array with 2x2 sub arrays.
I.e. this original array should become this.
Simply reshaping the array using x = x.reshape(4,4,2,2) results in this outcome
How do i overcome this?
Thanks
You need to do a little rearranging of the axes to match your expected outcome. In this case you've split each 8-length side into 4 blocks of 2, but then want the blocks of 2 in the final dimensions. So:
np.arange(64).reshape(4,2,4,2).transpose(0,2,1,3)
Out[]:
array([[[[ 0, 1],
[ 8, 9]],
[[ 2, 3],
[10, 11]],
[[ 4, 5],
[12, 13]],
[[ 6, 7],
[14, 15]]],
[[[16, 17],
[24, 25]],
[[18, 19],
[26, 27]],
[[20, 21],
[28, 29]],
[[22, 23],
[30, 31]]],
[[[32, 33],
[40, 41]],
[[34, 35],
[42, 43]],
[[36, 37],
[44, 45]],
[[38, 39],
[46, 47]]],
[[[48, 49],
[56, 57]],
[[50, 51],
[58, 59]],
[[52, 53],
[60, 61]],
[[54, 55],
[62, 63]]]])
Another way could be using the sliding_window:
y = np.lib.stride_tricks.sliding_window_view(x, (2,2))[::2, ::2]
y
array([[[[ 0, 1],
[ 8, 9]],
[[ 2, 3],
[10, 11]],
[[ 4, 5],
[12, 13]],
[[ 6, 7],
[14, 15]]],
[[[16, 17],
[24, 25]],

2D scatter with colormap effective on both axes - Python

I have been reading a lot of similar posts like here, here, here, and so on, yet I am not able to fix my problem. I need the colormap for my scatter plot to be effective on both axes, yet it can work on one axis only (in my example, axis "x"):
my_list = [[73, 84], [69, 84], [66, 84], [76, 83], [73, 83], [62, 84], [62, 83], [73, 79], [61, 84], [61, 83], [60, 90], [62, 79], [58, 84], [57, 90], [61, 79], [60, 79], [57, 84], [58, 83], [55, 84], [59, 79], [57, 83], [58, 79], [50, 84], [55, 83], [48, 84], [57, 79], [47, 84], [55, 79], [46, 93], [73, 78], [46, 84], [50, 83], [54, 79], [61, 78], [45, 88], [50, 79], [45, 84], [58, 78], [47, 83], [48, 79], [57, 78], [62, 20], [44, 84], [46, 83], [47, 79], [55, 78], [60, 20], [43, 84], [44, 83], [41, 84], [58, 20], [46, 79], [55, 25], [70, 15], [38, 95], [43, 83], [40, 84], [38, 89], [57, 20], [44, 79], [55, 24], [65, 15], [34, 100], [55, 20], [62, 19], [43, 79], [38, 84], [54, 24], [50, 78], [34, 95], [65, 13], [41, 83], [62, 13], [37, 84], [42, 79], [60, 19], [54, 20], [51, 24], [49, 78], [65, 10], [34, 90], [41, 79], [35, 84], [40, 83], [60, 15], [57, 19], [45, 78], [51, 20], [34, 88], [62, 10], [54, 19], [57, 15], [40, 79], [44, 78], [50, 20], [60, 10], [34, 84], [51, 19], [39, 79], [57, 10], [49, 20], [43, 78], [65, 8], [33, 84], [31, 88], [35, 83], [32, 84], [36, 79], [52, 15], [41, 78], [55, 10], [49, 19], [46, 74], [62, 8], [30, 90], [31, 84], [33, 83], [35, 79], [38, 78], [54, 10], [49, 15], [44, 74], [30, 88], [60, 8], [47, 19], [30, 84], [31, 83], [46, 20], [48, 15], [33, 79], [51, 10], [37, 78], [43, 25], [58, 8], [29, 90], [46, 19], [30, 83], [45, 20], [31, 79], [47, 15], [34, 78], [50, 10], [43, 24], [41, 74], [29, 84], [57, 8], [30, 79], [31, 78], [46, 15], [49, 10], [43, 20], [38, 24], [37, 74], [54, 8], [29, 83], [29, 79], [26, 84], [39, 20], [48, 10], [43, 15], [30, 78], [37, 24], [51, 8], [25, 90], [26, 83], [45, 10], [29, 78], [42, 15], [34, 74], [37, 20], [50, 8], [25, 84], [43, 13], [26, 79], [42, 14], [40, 15], [39, 19], [35, 20], [44, 10], [27, 78], [34, 24], [25, 83], [49, 8], [35, 19], [25, 79], [26, 78], [37, 15], [40, 13], [43, 10], [34, 20], [30, 74], [22, 84], [47, 8], [23, 79], [22, 83], [25, 78], [35, 15], [39, 10], [34, 19], [29, 74], [46, 8], [19, 90], [23, 78], [34, 15], [22, 79], [37, 10], [45, 8], [33, 20], [26, 74], [54, 5], [19, 88], [20, 79], [33, 19], [34, 14], [30, 20], [36, 10], [44, 8], [22, 78], [26, 25], [51, 5], [19, 84], [34, 13], [19, 79], [18, 84], [29, 20], [30, 19], [33, 15], [20, 78], [35, 10], [43, 8], [26, 24], [25, 74], [17, 88], [49, 5], [29, 19], [18, 79], [26, 20], [30, 15], [34, 10], [39, 8], [19, 78], [23, 24], [48, 5], [15, 88], [34, 9], [33, 10], [30, 14], [26, 15], [35, 8], [18, 78], [23, 20], [22, 74], [15, 84], [43, 5], [15, 83], [17, 78], [34, 8], [31, 10], [26, 14], [19, 74], [22, 20], [14, 84], [39, 5], [15, 79], [16, 78], [29, 10], [31, 8], [26, 13], [18, 74], [21, 20], [38, 5], [14, 83], [15, 78], [21, 19], [14, 79], [23, 15], [30, 8], [26, 10], [16, 74], [19, 20], [33, 5], [11, 84], [26, 9], [12, 79], [14, 78], [25, 10], [27, 8], [22, 15], [15, 74], [19, 19], [31, 5], [11, 83], [18, 19], [12, 78], [25, 8], [23, 10], [19, 15], [22, 14], [17, 20], [14, 74], [26, 5], [11, 79], [18, 15], [23, 9], [22, 10], [19, 14], [17, 19], [12, 74], [25, 5], [11, 78], [18, 14], [19, 10], [22, 9], [17, 15], [23, 5], [14, 20], [11, 74], [43, 3], [18, 10], [20, 9], [17, 14], [14, 19], [22, 5], [11, 24], [31, 3], [18, 8], [15, 14], [17, 10], [14, 15], [19, 5], [25, 3], [11, 20], [26, 0], [10, 78], [13, 15], [15, 10], [22, 3], [17, 5], [11, 19], [10, 74], [7, 84], [23, 0], [15, 8], [14, 10], [16, 5], [20, 3], [8, 74], [11, 15], [7, 78], [22, 0], [15, 5], [14, 8], [17, 3], [11, 13], [10, 20], [7, 74], [20, 0], [4, 79], [10, 15], [11, 10], [16, 3], [14, 5], [5, 78], [7, 20], [3, 88], [19, 0], [10, 14], [9, 15], [11, 9], [14, 3], [4, 78], [7, 19], [3, 79], [17, 0], [12, 5], [11, 8], [13, 3], [10, 10], [4, 74], [7, 15], [3, 78], [15, 0], [12, 3], [11, 5], [9, 10], [7, 14], [14, 0], [3, 20], [11, 3], [10, 5], [9, 9], [7, 10], [3, 19], [12, 0], [5, 10], [9, 5], [3, 15], [11, 0], [3, 14], [9, 3], [4, 10], [2, 19], [0, 84], [10, 0], [3, 10], [7, 8], [1, 78], [2, 14], [0, 79], [9, 0], [1, 74], [2, 10], [7, 0], [0, 78], [4, 5], [1, 10], [0, 74], [5, 0], [1, 8], [3, 5], [4, 0], [0, 10], [2, 3], [3, 0], [0, 8], [1, 3], [2, 0], [0, 5], [1, 0], [0, 3], [0, 0]]
x = [x[0] for x in my_list]
y = [x[1] for x in my_list]
plt.scatter(x, y, c=x, cmap='RdYlBu')
plt.colorbar()
As you see, the color map is working on "x" axis only. Now if I change my axis to "y", then this is what I would get:
What I need is a combination of these two .. that the color changes from red to blue from 0 to 100 on both axes. I have tried different ways and even different plots like imshow or heatmap, but scatter is what I need and I keep getting different errors. Could anyone help me to fix this please?
To color code based on both x and y values, one method is to use their vector sum (or distance from origin). First, you define the distance for each point. Then use that distance for color coding:
import numpy as np
d = [np.sqrt(i**2 + j**2) for i, j in zip(x, y)]
plt.scatter(x, y, c=d, cmap='RdYlBu')
plt.colorbar()

How can I delete the first 16 records on a List?

I want to get just 16 records and if there more than 16, then delete the first 16 from the list.
my code:
ItemList = {
"items": [
[
[19,1],[19,2],[19,3],[19,4],[19,5],[19,6],[19,7],[19,8],[19,9],[19,10],[19,11],[19,12],[19,13],[19,14],[19,15],[19,16],[19,17],[19,18],[19,19],[19,20],[19,21],[19,22],[19,23],[19,24],[19,25],[19,26],[19,27],[19,28],[19,29],[19,30],[19,31],[19,32],
],
[],
[],
],
}
if len(ItemList["items"][0]) > 16:
for index in xrange(16):
ItemList["items"][0].remove(ItemList["items"][0][index])
print ItemList["items"][0]
but it doesn't work
this is my output:
[[19, 2], [19, 4], [19, 6], [19, 8], [19, 10], [19, 12], [19, 14], [19, 16], [19, 18], [19, 20], [19, 22], [19, 24], [19, 26], [19, 28], [19, 30], [19, 32]]
I just want to get this:
[[19,17],[19,18],[19,19],[19,20],[19,21],[19,22],[19,23],[19,24],[19,25],[19,26],[19,27],[19,28],[19,29],[19,30],[19,31],[19,32]]
(Edit after comment)
May be this is what you are looking for:
if len(ItemList['items'][0]) > 16:
ItemList['items'][0] = ItemList['items'][0][-16:]
You can use this:
ItemList['items'][0] = ItemList['items'][0][16:]
Just:
del ItemList["items"][0][:16]
You could just use slices:
>>> ItemList['items'][0][16::]
[[19, 17], [19, 18], [19, 19], [19, 20], [19, 21], [19, 22], [19, 23], [19, 24], [19, 25], [19, 26], [19, 27], [19, 28], [19, 29], [19, 30], [19, 31], [19, 32]]

Python: Sort list by date?

Is it possible to sort this list by date? It is for a flot graph so it has to be organized in this format of a list of smaller list pairs. I would like to be able to sort it by date.
[["2014-5-29", 19], ["2014-5-28", 16], ["2014-5-30", 20], ["2014-5-23", 16], ["2014-5-22", 1225], ["2014-5-21", 114], ["2014-5-20", 69], ["2014-5-27", 10], ["2014-5-31", 17], ["2014-5-25", 18], ["2014-5-24", 19], ["2014-5-26", 18], ["2014-6-2", 19], ["2014-6-1", 19], ["2014-5-18", 4], ["2014-5-19", 27]]
Thanks!
Here is one way:
In [9]: l = [["2014-5-29", 19], ["2014-5-28", 16], ["2014-5-30", 20], ["2014-5-23", 16], ["2014-5-22", 1225], ["2014-5-21", 114], ["2014-5-20", 69], ["2014-5-27", 10], ["2014-5-31", 17], ["2014-5-25", 18], ["2014-5-24", 19], ["2014-5-26", 18], ["2014-6-2", 19], ["2014-6-1", 19], ["2014-5-18", 4], ["2014-5-19", 27]]
In [10]: sorted(l, key=lambda (date, _): map(int, date.split('-')))
Out[10]:
[['2014-5-18', 4],
['2014-5-19', 27],
['2014-5-20', 69],
['2014-5-21', 114],
['2014-5-22', 1225],
['2014-5-23', 16],
['2014-5-24', 19],
['2014-5-25', 18],
['2014-5-26', 18],
['2014-5-27', 10],
['2014-5-28', 16],
['2014-5-29', 19],
['2014-5-30', 20],
['2014-5-31', 17],
['2014-6-1', 19],
['2014-6-2', 19]]

minimum of list of lists

I have a list of lists like so:
[[10564, 15], [10564, 13], [10589, 18], [10637, 39], [10662, 38], [10712, 50], [10737, 15], [10762, 14], [10787, 9], [10812, 12], [10837, 45], [3, 17], [7, 21], [46, 26], [48, 12], [49, 24], [64, 14], [66,
17], [976, 27], [981, 22], [982, 22], [983, 17], [985, 13], [517, 9], [521, 15], [525, 11], [526, 13], [528, 14], [698, 14], [788, 24], [792, 19]]
I am trying to find the lowest value for the second element in each list(so compare 15 to 13 to 18 etc not comparing 10564 and 15 ), but also to separate it into ranges, so I could say, lowest second element[1] in each list, only if element[0] is over 10000 etc. How might I do this? I tried it and can only compare elements in the same list as of yet, which is not what I want. In the case I mentions I would then be returning [10787, 9] but if there was another value over 10000 with 9 I would want to return that also.
This depends on what you want for output. First, you'll need to filter your list based on the "ranges" 1
gen = (x for x in lists if x[0] > 10000)
The if condition can be as complicated as you want (within valid syntax). e.g.:
gen = (x for x in lists if 5000 < x[0] < 10000)
Is perfectly fine.
Now, If you want only the second element from the sublists:
min(x[1] for x in gen)
Of course, you could inline the whole thing:
min(x[1] for x in lists if x[0] > 10000)
If you want the entire sublist:
from operator import itemgetter
min(gen,key=itemgetter(1))
example:
>>> lists = [[10564, 15], [10564, 13], [10589, 18], [10637, 39], [10662, 38], [10712, 50], [10737, 15], [10762, 14], [10787, 9], [10812, 12], [10837, 45], [3, 17], [7, 21], [46, 26], [48, 12], [49, 24], [64, 14], [66,17], [976, 27], [981, 22], [982, 22], [983, 17], [985, 13], [517, 9], [521, 15], [525, 11], [526, 13], [528, 14], [698, 14], [788, 24], [792, 19]]
>>> gen = (x for x in lists if x[0] > 10000)
>>> min(x[1] for x in gen)
9
>>> gen = (x for x in lists if x[0] > 10000)
>>> from operator import itemgetter
>>> min(gen,key=itemgetter(1))
[10787, 9]
Unfortunately, these only give you the first sublist which matches the criteria. To get all of them:
target = min(x[1] for x in lists if x[0] > 10000)
matches = [x for x in lists if (x[1] == target) and (x[0] > 10000)]
If you know for sure that there will be less than N matches, you could do this a little more efficiently with heapq and itertools.takewhile. In the general case where you don't know an upper limit on the number of matches, I think this solution is better (It's O(N) compared to sorting which is O(NlogN)).
1Note that the "generator expression" can only be iterated over once before it is exhausted
Here's a very simply approach that just finds the minimum value and then builds the list based on that value.
>>> a = [[10564, 15], [10564, 13], [10589, 18], [10637, 39], [10662, 38], [10712, 50], [10737, 15], [10762, 14], [10787, 9], [10812, 12], [10837, 45], [3, 17], [7, 21], [46, 26], [48, 12], [49, 24], [64, 14], [66,
... 17], [976, 27], [981, 22], [982, 22], [983, 17], [985, 13], [517, 9], [521, 15], [525, 11], [526, 13], [528, 14], [698, 14], [788, 24], [792, 19]]
>>> a_min = min(i[1] for i in a)
>>> [i[0] for i in a if i[1] == a_min and i[0] > 10000] + [a_min]
[10787, 9]
The code correctly displays multiple values:
>>> a += [[10391, 9]] #add another pair with a first value > 10000
>>> [i[0] for i in a if i[1] == a_min and i[0] > 10000] + [a_min]
[10787, 10391, 9]
>>> l=[[10564, 15], [10564, 13], [10589, 18], [10637, 39]]
>>> min(x[1] for x in l if x[0] > 10000)
13
>>>
update for your comment (you can use lambda for key in min function, itemgetter a little faster on large lists):
>>> min((x for x in l if x[0] > 10000), key=lambda k:k[1])
[10564, 13]
If you require multiple mins, then perhaps you're best of filtering applicable elements and sorting them...
vals = sorted((el for el in your_list if el[0] >= 10000), key=lambda L: L[1])
# [[10787, 9], [10812, 12], [10564, 13], [10762, 14], [10564, 15], [10737, 15], [10589, 18], [10662, 38], [10637, 39], [10837, 45], [10712, 50]]
Then you can take vals[0] to get the first, vals[1] to get the second, or use slicing such as vals[:5]...
a=[[10564, 15], [10564, 13], [10589, 18], [10637, 39], [10662, 38], [10712, 50], [10737, 15], [10762, 14], [10787, 9], [10812, 12], [10837, 45], [3, 17], [7, 21], [46, 26], [48, 12], [49, 24], [64, 14], [66, 17], [976, 27], [981, 22], [982, 22], [983, 17], [985, 13], [517, 9], [521, 15], [525, 11], [526, 13], [528, 14], [698, 14], [788, 24], [792, 19]]
print min(map(lambda y: y[1] ,filter(lambda x: x[0]>10000,a)))

Categories