Python find indices in an array based on conditions - python

I have a list/array(can make them arrays, doesn't matter):
array = [1,2,1,1,2,3,1,2,1,2,3,1,2,3,4,5]
I would like to create a new_array FROM array which looks like this:
new_array = [[1,2],[1],[1,2,3],[1,2],[1,2,3],[1,2,3,4,5]]
The logic I came up with was to start with a=0,b=1 and loop through array, when the value of array[a] < array[b], then a+=1,b+=1, but if the value of array[a]>=array[b], then append the value of b:
Here is what I tried:
index_1 = [] # This will be the list which will have the FIRST index
index_2 = [] # This will be the list which will have the SECOND index
a = 0
b = 1
for i in array:
if i[a] < i[b]:
a = a+1
b = b+1
elif i[a] >= i[b]:
index_2.append(b)
index_1.append(a)
So index_1 will have the first index and index_2 will have the second index and then, we can create the new_array by:
new_array = [(array[start:end]) for start,end in zip(index_1,index_2)]
Unfortunately, even though my idea is correct, the loop stops in if i[a]<i[b] because of IndexError: invalid index to scalar variable..

We can go through the array directly by the elements (more precisely by a pair of neighboring elements) simultaneously forming the result.
We initialize our result with a sublist of the 1st element of the array and then:
if the next element is greater than the previous one, we add it to the last sublist
otherwise - we add a new sublist with this element to the result.
array = [1,2,1,1,2,3,1,2,1,2,3,1,2,3,4,5]
new_array = [[array[0]]] # assume the array is not empty
for a,b in zip(array, array[1:]):
if a<b:
new_array[-1].append(b)
else:
new_array.append([b])
print(new_array)
# [[1, 2], [1], [1, 2, 3], [1, 2], [1, 2, 3], [1, 2, 3, 4, 5]]
UPD.
Alternatively, we can implement it with indices.
(based on this answer by #fortran)
First, prepare indices to split the array:
idxs_to_split = [idx for idx in range(1, len(array)) if not array[idx-1] < array[idx]]
pairs = zip([0] + idxs_to_split, idxs_to_split + [None])
Second, implement the splitting itself:
new_array = [array[i:j] for i,j in pairs]
print(new_array)
# [[1, 2], [1], [1, 2, 3], [1, 2], [1, 2, 3], [1, 2, 3, 4, 5]]

Here's a simple approach, just keep track of the previous, and check if it is smaller. If it is, append to sub. If it isnt, append sub to the new list, then create a new sub. Always append sub at the end.
>>> new_array = []
>>> array = [1,2,1,1,2,3,1,2,1,2,3,1,2,3,4,5]
>>> sub = []
>>> prev = float('-inf')
>>> for x in array:
... if prev < x:
... sub.append(x)
... else:
... new_array.append(sub)
... sub = [x]
... prev = x
...
>>> new_array.append(sub)
>>> new_array
[[1, 2], [1], [1, 2, 3], [1, 2], [1, 2, 3], [1, 2, 3, 4, 5]]

def g(l):
acc = []
for i in l:
if not acc:
acc.append(i)
else:
if acc[-1] < i:
acc.append(i)
else:
yield acc
acc = [i]
yield acc
Here's a generator version that should work for all iterables. Usage would be something like
list(g(array))
And gives the desired
[[1, 2], [1], [1, 2, 3], [1, 2], [1, 2, 3], [1, 2, 3, 4, 5]]

I simply checked for two conditions, 1. If next element == 1 and 2. If j is at last element of the list.
array = [1,2,1,1,2,3,1,2,1,2,3,1,2,3,4,5]
newList = []
j = 0
k = 0
while j < len(array)-1:
if array[j+1] ==1:
newList.append(array[k:j+1])
k = j+1
if j+2==len(array):
newList.append(array[k:j+2])
k = j + 1
j +=1
print newList

Related

Change Data Structure of an array (new ID and replace Duplicates in loop)

I'm new in the "python game", so maybe the following question is for some of you very easy to answer:
I have an array like this:
store=([9,4,5],[9,4,1],[1,2,3],[9,4,1],[3,7,5],[2,4,1])
I want to "loop" the array and creat a new structure:
store_new=([0,1,2],[0,1,3],[3,4,5],[0,1,3],[5,6,2],[4,1,3])
The first value starts with 0, the second is 1 and so on. If there is a duplicate it should take the same new value as before. For example for "9"(old) to "0"(new)....and for the next "9"(old) again to "0" (new). Is there a nice way to handle this problem?
I tried something with “enumerate” in a “for loop”...but this don't really work.
You can use itertools.count and dict.setdefault in a list comprehension:
store=([9,4,5],[9,4,1],[1,2,3],[9,4,1],[3,7,5],[2,4,1])
from itertools import count
d = {}
c = count()
out = tuple([d[x] if x in d else d.setdefault(x, next(c)) for x in l]
for l in store)
Output:
([0, 1, 2], [0, 1, 3], [3, 4, 5], [0, 1, 3], [5, 6, 2], [4, 1, 3])
With a classical python loop:
out = []
d = {}
max_val = 0
for l in store:
out.append([])
for x in l:
if x in d:
out[-1].append(d[x])
else:
d[x] = max_val
out[-1].append(max_val)
max_val += 1
out = tuple(out)

How to get all consecutive sequences of numbers from given set of numbers?

I will start the description of the problem with an example so that the question is clear.
List of numbers: [1,2,3,4]
How can I get each successive string: [[1],[1,2],[1,2,3],[1,2,3,4],[2],[2,3],[2,3,4],[3],[3,4],[4]]
I've been trying to solve this problem for a while and managed to get [[1, 2, 3, 4], [2, 3, 4], [3, 4], [4]] with lots of useless repetitions.
list = [1,2,3,4]
i = 0
j = 0
tempList = []
sequancesList = []
while i < len(list):
while j < len(list):
tempList.append(list[j])
sequancesList.append(tempList)
j += 1
i += 1
j = i
tempList = []
def deleteDuplicates(list):
noduplist = []
for element in list:
if element not in noduplist:
noduplist.append(element)
return noduplist
finalSequancesList = deleteDuplicates(sequancesList)
print(finalSequancesList)
Here's how to do it:
list = [1,2,3,4]
sequancesList = []
for i in range(len(list)):
tmp = []
for j in range(i,len(list)):
tmp.append(list[j])
sequancesList.append(tmp[:])
print(sequancesList)
-> [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [2], [2, 3], [2, 3, 4], [3], [3, 4], [4]]
Here is a generator that does exactly that:
def iter_successive(input_list):
for i in range(len(input_list)):
for j in range(i+1,len(input_list)+1):
yield input_list[i:j]
>>> list(iter_successive(input_list))
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [2], [2, 3], [2, 3, 4], [3], [3, 4], [4]]
Comparable one-liner solution:
def iter_successive(input_list):
return (input_list[i:j] for i in range(len(input_list))
for j in range(i+1,len(input_list)+1))
Edit: As was stated in the comments, the approach below works only if the list contains whole, ascending numbers and not in other instances.
There are two ways to do this, either as a nested for-loop or a one-liner using list-comprehension. Both versions below:
Nested for-loop
nrs = list(range(1,5))
result = []
for i in nrs:
for j in range(i, max(nrs)+1):
result.append(list(range(i,j+1)))
print(result)
List-comprehension
result = [list(range(i,j+1)) for i in nrs for j in range(i, max(nrs)+1)]
print(result)

How to merge smaller sub-elements into larger parent-elements in a list?

I have a list of lists, but some lists are "sublists" of other lists. What I want to do is remove the sublists from the larger list so that we only have the largest unique sublists.
For example:
>>> some_list = [[1], [1, 2], [1, 2, 3], [1, 4]]
>>> ideal_list = [[1, 2, 3], [1, 4]]
The code that I've written right now is:
new_list = []
for i in range(some_list)):
for j in range(i + 1, len(some_list)):
count = 0
for k in some_list[i]:
if k in some_list[j]:
count += 1
if count == len(some_list[i]):
new_list.append(some_list[j])
The basic algorithm that I had in mind is that we'd check if a list's elements were in the following sublists, and if so then we use the other larger sublist. It doesn't give the desired output (it actually gives [[1, 2], [1, 2, 3], [1, 4], [1, 2, 3]]) and I'm wondering what I could do to achieve what I want.
I don't want to use sets because duplicate elements matter.
Same idea as set, but using Counter instead. It should be a lot more efficient in sublist check part than brute force
from collections import Counter
new_list = []
counters = []
for arr in sorted(some_list, key=len, reverse=True):
arr_counter = Counter(arr)
if any((c & arr_counter) == arr_counter for c in counters):
continue # it is a sublist of something else
new_list.append(arr)
counters.append(arr_counter)
With some inspiration from #mkrieger1's comment, one possible solution would be:
def merge_sublists(some_list):
new_list = []
for i in range(len(some_list)):
true_or_false = []
for j in range(len(some_list)):
if some_list[j] == some_list[i]:
continue
true_or_false.append(all([x in some_list[j] for x in some_list[i]]))
if not any(true_or_false):
new_list.append(some_list[i])
return new_list
As is stated in the comment, a brute-force solution would be to loop through each element and check if it's a sublist of any other sublist. If it's not, then append it to the new list.
Test cases:
>>> merge_sublists([[1], [1, 2], [1, 2, 3], [1, 4]])
[[1, 2, 3], [1, 4]]
>>> merge_sublists([[1, 2, 3], [4, 5], [3, 4]])
[[1, 2, 3], [4, 5], [3, 4]]
Input:
l = [[1], [1, 2], [1, 2, 3], [1, 4]]
One way here:
l1 = l.copy()
for i in l:
for j in l:
if set(i).issubset(set(j)) and i!=j:
l1.remove(i)
break
This prints:
print(l1)
[[1, 2, 3], [1, 4]]
EDIT: (Taking care of duplicates as well)
l1 = [list(tupl) for tupl in {tuple(item) for item in l }]
l2 = l1.copy()
for i in l1:
for j in l1:
if set(i).issubset(set(j)) and i!=j:
l2.remove(i)
break

How can I find same values in a list and group together a new list?

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]]

For each matching element in two lists add a list and extend

For some reason my brain's not working.
I have two sorted list-like objects:
>>> a = [1,2,3,4]
>>> b = [1,1,2,2,2,3,3,3]
From this, I need to get:
>>> a = [ [1,[1,1]], [2,[2,2,2]], [3,[3,3,3]], 4]
Codewise, I was thinking this...
>>> i = 0
>>> first = True
>>> for num in b:
... if num == a[i]:
... if first:
... a[i] = [a[i],[num]]
... first = False
... else:
... a[i][1].append(num)
... else:
... first = True
... if a[i+1] == num:
... a[i] = [a[i+1],[num]]
... else:
... print 'problem'
But I keep getting confused. What is an efficient way to solve this? I feel like recursion, maybe, but I can't figure that out either.
As the data is already sorted, you can group b based on the number and create a dictionary using dictionary comprehension. In the next pass over the keys (a) you can get the elements from the dictionary corresponding to the elements of a and create a new list.
a, b = [1,2,3,4], [1,1,2,2,2,3,3,3]
from itertools import groupby
d = {k:list(grp) for k, grp in groupby(b)}
# {1: [1, 1], 2: [2, 2, 2], 3: [3, 3, 3]}
print [[k, d[k]] if k in d else k for k in a]
# [[1, [1, 1]], [2, [2, 2, 2]], [3, [3, 3, 3]], 4]
I would do this:
from collections import Counter
a = [1,2,3,4]
b = [1,1,2,2,2,3,3,3]
bb = Counter(b)
new_list = [[el_a,[el_a for _ in range(bb[el_a])]] for el_a in a]
# [[1, [1, 1]], [2, [2, 2, 2]], [3, [3, 3, 3]], [4, []]]
An easy-reading option:
finallist = []
a = [1,2,3,4]
b = [1,1,2,2,2,3,3,3]
pos = 0
for item in a:
mylist = [item,[]]
while pos < len(b) and b[pos] == item :
mylist[1].append(item)
pos += 1
finallist.append(mylist)
print finallist

Categories

Resources