Python lists (getting triplets of identical numbers from the list) - python

Assume I have a list [12,12,12,12,13,13,13,13,14,14,14,14,14,14,14,15,15,15, etc]
I would like my result to be the following:
[12,12,12,13,13,13,14,14,14,15,15,15]
The number of identical numbers in the first list can vary, but I want to get triplets for each range of the identical numbers. I assume I could iterate through the list starting from the first number (12) and get the first 3 identical numbers (12,12,12), and then compare the numbers and once the number 12 changes to 13, get the next 3 numbers (13,13,13), and so on. But I cannot think of a good approach to do it correctly. Thank you for any suggestions.

I would use itertools.groupby() to isolate the strings of identical numbers, then use a list comprehension to create the triplets:
import itertools
some_list = [12,12,12,12,13,13,13,13,14,14,14,14,14,14,14,15,15,15,]
updated_list = [i for k,_ in itertools.groupby(some_list) for i in [k]*3]
assert updated_list == [12,12,12,13,13,13,14,14,14,15,15,15]

updated_list = []
curr_number = some_list[0]
curr_count = 0
for n in some_list:
if n == curr_number
curr_count += 1
if not (curr_count > 3):
updated_list.append(n)
else:
curr_number = n
curr_count = 1
updated_list.append(n)

Seems as a set approach is a bit faster than itertools. If you need it sorted, less but still faster.
A = [12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15]
def with_set(A):
A = set(A)
return list(A) * 3
import itertools
def with_iter(A):
return [i for k,_ in itertools.groupby(A) for i in [k]*3]
import timeit
print("Result with set: ", timeit.timeit(lambda:with_set(A),number = 1000))
print("Result with iter: ", timeit.timeit(lambda:with_iter(A),number = 1000))
Result with set: 0.008438773198370306
Result with iter: 0.018557160246834882

Below line of code is self explanatory:
A = [1, 2, 3, 4, 1, 4]
A = list(set(A)) #removes duplicates
A *= 3 #multiplies the unique list 3 times
print sorted(A) # makes a new sorted list

Related

Splitting a list of lists in specific intervals in Python

I have a really big list of lists with integers that is also sorted from low to high. Also every nested list is an arithmetic sequence increasing by 1 . To make it more clear it could look something like:
f = [[0,1], [3], [7,8,9,10,11,12], [15,16], [18], [22,23,24], [39,40,41], [49,50,51]]
My goal is to split the nested big list into smaller nested lists. My first list must have numbers between 0 and 10, my second list must have numbers between 20 and 30 , my third between 40 to 50 etc. So I was wondering if there is a way to code in python to get the following lists:
f1 = [[0,1],[3],[7,8,9,10]]
f2 = [[22,23,24]]
f3 = [[40,41],[49,50]]
Here is one way to do so:
data = []
for i in range(0, f[-1][-1], 20):
new_seqs = []
for seq in f:
if i - len(seq) + 1 <= seq[0] <= i + 10:
new_nums = []
for num in seq:
if i <= num <= i + 10:
new_nums.append(num)
new_seqs.append(new_nums)
data.append(new_seqs)
print(data)
The same using list comprehension:
data = [[[num for num in seq if i <= num <= i + 10] for seq in f if i - len(seq) + 1 <= seq[0] <= i + 10] for i in range(0, f[-1][-1], 20)]
print(data)
Output:
[[[0, 1], [3], [7, 8, 9, 10]], [[22, 23, 24]], [[40, 41], [49, 50]]]
We run a for loop from 0 to the largest element in the list (f[-1][-1]), increasing by 20 each time.
For each sub-list we check if at least one element is included between i and i + 10. As it is an arithmetic sequence of common difference 1, we only have to compare the first term.
If there is at least one term in the interval, we compare each number with i and i + 10.
I guess, similar to this is what you are looking for?
from collections import defaultdict
mem = defaultdict(list)
# expectations: each sub-list is stickly between x * 10 and (x+1) * 10 where x is a number
def find_list_number(num, i=10):
if num < i:
return int(i / 10)
else:
return find_list_number(num, i + 10)
for sub_list in flist:
my_max = max(sub_list)
key = find_list_number(my_max)
mem[key].append(sub_list)
for k, v in mem.items():
print((k, v))
Sample Output for above
(1, [[0, 1], [3]])
(2, [[7, 8, 9, 10, 11, 12], [15, 16], [18]])
(3, [[22, 23, 24]])
(5, [[39, 40, 41]])
(6, [[49, 50, 51]])
Note: [7,8,9,10,11,12] is in different class - not bug here. But you can modify conditions as you need & add additional conditions as it suits you. This is a sample, only to guide you.

how to print elements of a list whose sum is equal to a given number in python?

Consider a list of numbers from 0 to 50. I want to print all the combinations of elements from that list whose sum is equal to any given number say 41.
One combination is [4,5,7,9,16].I want to print other combinations like this.
You can use combinations(...) function from itertools
import itertools
nums = range(51)
for n in range(1, len(nums) + 1):
for p in itertools.combinations(nums, n):
if sum(p) == 41:
print(*p)
The following code is best used in a subroutine but it can be stand alone code.To change the average just change the number after the while loop. To change the amount of numbers change the amount of zeros in the lists. The random part can also be changed for the range of numbers.
def stats():
x =[0,0,0,0,0,0]
while mean(x) != 14.5:
x =[0,0,0,0,0,0]
for i in range(6):
a = random.randint(9,17)
x[i] = a
return x
The mean in this code is a separate subroutine that looks like this:
def mean(x):
y = sum(x)/6
return y
At the start of the code, you need to import the random library to allow the code to work properly. The only thing is that this code will only output one combination,such as:
stats()
[11, 12, 16, 17, 16, 15]
You could write a recursive generator to efficiently produce only the combinations of positive integers that sum up to the target number:
def sumSets(numbers,target):
if not target : yield []
if not numbers or target <= 0: return
yield from sumSets(numbers[1:],target)
yield from (numbers[:1]+ss for ss in sumSets(numbers[1:],target-numbers[0]))
nums = list(range(1,50,3))
for ss in sumSets(nums,41): print(ss)
[19, 22]
[16, 25]
[13, 28]
[10, 31]
[7, 34]
[4, 37]
[1, 40]
[1, 4, 7, 13, 16]
[1, 4, 7, 10, 19]
Note that, if you're looking for all combinations of numbers from 1 to 50 that sum up to 41, you're going to get a lot of them:
nums = list(range(1,51))
print(sum(1 for _ in sumSets(nums,41))) # 1260

How to cumsum all values at tuple index n, from a list of tuples?

In the following code I want to add second parameters of list=[(0,3),(2,6),(1,10)] in a for loop. first iteration should be 3+6=9 and the second iteration should add output of previous iteration which is 9 to 10---> 9+10=19 and I want final output S=[9,19]. I am not sure how to do it, Should I add another loop to my code?
T=[(0,3),(2,6),(1,10)]
S=[]
for i in range(len(T)):
b=T[0][i]+T[0][i+1]
S.append(b)
use itertools.accumulate
spam = [(0,3),(2,6),(1,10)]
from itertools import accumulate
print(list(accumulate(item[-1] for item in spam))[1:])
output
[9, 19]
Use zip to combine the vales from the tuples, with the same index.
Use an assignment expression (from python 3.8), in a list-comprehension, to sum the values in the second tuple, T[1], of T.
T = [(0,3),(2,6),(1,10)]
T = list(zip(*T))
print(T)
[out]:
[(0, 2, 1), (3, 6, 10)]
# use an assignment expression to sum T[1]
total = T[1][0] # 3
S = [total := total + v for v in T[1][1:]]
print(S)
[out]:
[9, 19]
Just modify your code as below
T=[(0,3),(2,6),(1,10)]
S=[]
b = T[0][1]
for i in range(1,len(T)):
b+=T[i][1]
S.append(b)
This should help u:
T=[(0,3),(2,6),(1,10)]
lst = [T[i][1] for i in range(len(T))]
final_lst = []
for x in range(2,len(lst)+1):
final_lst.append(sum(lst[:x]))
print(final_lst)
Output:
[9, 19]
If u prefer list comprehension, then use this line instead of the last for loop:
[final_lst.append(sum(lst[:x])) for x in range(2,len(lst)+1)]
Output:
[9, 19]
Here is a solution with native recursion
import operator
mylist =[(0,3),(2,6),(1,10)]
def accumulate(L, i, op):
def iter(result, rest, out):
if rest == []:
return out
else:
r = op(result, rest[0][i-1])
return iter(r, rest[1:], out + [r])
return iter(L[0][i-1], L[1:], [])
print(accumulate(mylist, 2, operator.add))
print(accumulate(mylist, 1, operator.add))
print(accumulate(mylist, 2, operator.mul))
# ==>
# [9, 19]
# [2, 3]
# [18, 180]

PYTHON - finding the maximum of every 10 integers in an array

I have a large array of integers, and I need to print the maximum of every 10 integers and its corresponding index in the array as a pair.
ex. (max_value, index of max_value in array)
I can successfully find the maximum value and the corresponding index within the first 10 integers, however I am having trouble looping through the entire array.
I have tried using:
a = some array of integers
split = [a[i:i+10] for i in xrange(0, len(a), 10)]
for i in split:
j = max(i)
k = i.index(max(i))
print (j,k)
The issue with this method is that it splits my array into chunks of 10 so the max_values are correct, but the indexes are inaccurate (all of the indexes are between 0-10.)
I need to find a way of doing this that doesn't split my array into chunks so that the original indices are retained. I'm sure there is an easier way of looping through to find max values but I can't seem to figure it out.
A small modification to your current code:
a = some array of integers
split = [a[i:i+10] for i in xrange(0, len(a), 10)]
for index, i in enumerate(split):
j = max(i)
k = i.index(max(i))
print (j, k+10*index)
You need to count the number of elements that appear before the current window. This will do the job:
a=list(range(5,35))
split = [a[i:i+10] for i in xrange(0, len(a), 10)]
for ind,i in enumerate(split):
j = max(i)
k = i.index(j)
print (j,k+ind*10)
This prints
(14, 9)
(24, 19)
(34, 29)
So with debugging with an example array, we find that split returns a 2d list like this one:
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]]
And every time the for loop runs, it does through one of those lists in order. First it goes through the first inner list then the second one etc. So every time the for loop jumps into the next list, we simply add 10. Since the list can have over 2 lists in them, we store the number we need to add in a variable and add 10 to it every loop:
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
split = [a[i:i+10] for i in xrange(0, len(a), 10)]
counter = 0
for i in split:
j = max(i)
k = i.index(max(i))
print (j,k+counter)
counter += 10
You can test it here
The toolz package has a partition_all function that divides a sequence up into equal-sized tuples, so you can do something like this.
import toolz
ns = list(range(25))
[max(sublist) for sublist in toolz.partition_all(10, ns)]
This will return [9, 19, 24].
You will need to loop through in order to iterate through the list, however we could change your split's loop to make it more effective to what you want.
a = some array of integers
split = [a[i:i+10] for i in xrange(0, len(a), 10)]
for i in range(len(split)):
#Now instead of being the list, i is the index, so we can use 10*i as a counter
j = max(split[i])
#j = max(i)
k = split[i].index(j) + 10*i #replaced max(i) with j since we already calculated it.
#k = i.index(max(i))
print (j,k)
Though in the future, please make a new name for your split list since split is already a function in python. Perhaps split_list or separated or some other name that doesn't look like the split() function.
numpy solution for arbitrary input:
import numpy as np
a = np.random.randint(1,21,40) #40 random numbers from 1 to 20
b = a.reshape([4,10]) #shape into chunks 10 numbers long
i = b.argsort()[:,-1] #take the index of the largest number (last number from argsort)
# from each chunk. (these don't take into account the reshape)
i += np.arange(0,40,10) #add back in index offsets due to reshape
out = zip(i, a[i]) #zip together indices and values
You could simplify this by only enumerating once and using zip to partition your list into groups:
n=10
for grp in zip(*[iter(enumerate(some_list))]*n):
grp_max_ind, grp_mv=max(grp, key=lambda t: t[1])
k=[t[1] for t in grp].index(grp_mv)
print grp_mv, (grp_max_ind, k)
Use izip in Python 2 if you want a generator (or use Python 3)
from itertools import izip
for grp in izip(*[iter(enumerate(some_list))]*n):
grp_max_ind, grp_mv=max(grp, key=lambda t: t[1])
k=[t[1] for t in grp].index(grp_mv)
print grp_mv, (grp_max_ind, k)
Zip will truncate the last group if not a length of n
An example using numpy. First let's generate some data, i.e., integers ranging from 1 to V and of length (number of values) L:
import numpy as np
V = 1000
L = 45 # method works with arrays not multiples of 10
a = np.random.randint(1, V, size=L)
Now solve the problem for sub-arrays of size N:
import numpy as np
N = 10 # example "split" size
sa = np.array_split(a, range(N, len(a), N))
sind = [np.argpartition(i, -1)[-1] for i in sa]
ind = [np.ravel_multi_index(i, (len(sa), N)) for i in enumerate(sind)]
vals = np.asarray(a)[np.asarray(ind)]
split_imax = zip(vals, ind) # <-- output

Split a Python list logarithmically

I am trying to do the following..
I have a list of n elements. I want to split this list into 32 separate lists which contain more and more elements as we go towards the end of the original list. For example from:
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
I want to get something like this:
b = [[1],[2,3],[4,5,6,7],[8,9,10,11,12]]
I've done the following for a list containing 1024 elements:
for i in range (0, 32):
c = a[i**2:(i+1)**2]
b.append(c)
But I am stupidly struggling to find a reliable way to do it for other numbers like 256, 512, 2048 or for another number of lists instead of 32.
Use an iterator, a for loop with enumerate and itertools.islice:
import itertools
def logsplit(lst):
iterator = iter(lst)
for n, e in enumerate(iterator):
yield itertools.chain([e], itertools.islice(iterator, n))
Works with any number of elements. Example:
for r in logsplit(range(50)):
print(list(r))
Output:
[0]
[1, 2]
[3, 4, 5]
[6, 7, 8, 9]
... some more ...
[36, 37, 38, 39, 40, 41, 42, 43, 44]
[45, 46, 47, 48, 49]
In fact, this is very similar to this problem, except it's using enumerate to get variable chunk sizes.
This is incredibly messy, but gets the job done. Note that you're going to get some empty bins at the beginning if you're logarithmically slicing the list. Your examples give arithmetic index sequences.
from math import log, exp
def split_list(_list, divs):
n = float(len(_list))
log_n = log(n)
indices = [0] + [int(exp(log_n*i/divs)) for i in range(divs)]
unfiltered = [_list[indices[i]:indices[i+1]] for i in range(divs)] + [_list[indices[i+1]:]]
filtered = [sublist for sublist in unfiltered if sublist]
return [[] for _ in range(divs- len(filtered))] + filtered
print split_list(range(1024), 32)
Edit: After looking at the comments, here's an example that may fit what you want:
def split_list(_list):
copy, output = _list[:], []
length = 1
while copy:
output.append([])
for _ in range(length):
if len(copy) > 0:
output[-1].append(copy.pop(0))
length *= 2
return output
print split_list(range(15))
# [[0], [1, 2], [3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14]]
Note that this code is not efficient, but it can be used as a template for writing a better algorithm.
Something like this should solve the problem.
for i in range (0, int(np.sqrt(2*len(a)))):
c = a[i**2:min( (i+1)**2, len(a) )]
b.append(c)
Not very pythonic but does what you want.
def splitList(a, n, inc):
"""
a list to split
n number of sublist
inc ideal difference between the number of elements in two successive sublists
"""
zr = len(a) # remaining number of elements to split into sublists
st = 0 # starting index in the full list of the next sublist
nr = n # remaining number of sublist to construct
nc = 1 # number of elements in the next sublist
#
b=[]
while (zr/nr >= nc and nr>1):
b.append( a[st:st+nc] )
st, zr, nr, nc = st+nc, zr-nc, nr-1, nc+inc
#
nc = int(zr/nr)
for i in range(nr-1):
b.append( a[st:st+nc] )
st = st+nc
#
b.append( a[st:max(st+nc,len(a))] )
return b
# Example of call
# b = splitList(a, 32, 2)
# to split a into 32 sublist, where each list ideally has 2 more element
# than the previous
There's always this.
>>> def log_list(l):
if len(l) == 0:
return [] #If the list is empty, return an empty list
new_l = [] #Initialise new list
new_l.append([l[0]]) #Add first iteration to new list inside of an array
for i in l[1:]: #For each other iteration,
if len(new_l) == len(new_l[-1]):
new_l.append([i]) #Create new array if previous is full
else:
new_l[-1].append(i) #If previous not full, add to it
return new_l
>>> log_list([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
[[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]

Categories

Resources