I have an list, sum the similar number - python

list = [1,1,4,4,4,0,1]
new_list = []
sum_ = 0
for number in list:
if number == number+1:
sum_ += number
else:
sum_ += number
new_list.append(sum_)
print(new_list)
Output => [1, 2, 6, 10, 14, 14, 15]
Expected => [2, 12, 0, 1]

Check this code:
my_list = [1,1,4,4,4,0,1]
my_sum = my_list[0]
my_results = []
for i in range(1, len(my_list)):
if my_list[i] == my_list[i-1]:
my_sum += my_list[i]
else:
my_results.append(my_sum)
my_sum = my_list[i]
else:
my_results.append(my_sum)
I first initialize my_sum to the first element of the list, and then I sweep over the remaining elements of the list, always comparing adjacent elements for equality. If they are equal, my_sum us updated, and if they are not equal my_sum is first stored to the output list my_results and then reinitialized to a new number from the original list.

The code counts the number of consecutive identical numbers and multiplies these numbers by their number
numbers = [1, 1, 4, 4, 4, 0, 1]
hook, cnt, out = numbers[0], 0, []
for i in numbers:
if i == hook:
cnt += 1
else:
out.append(hook * cnt)
hook, cnt = i, 1
out.append(hook * cnt)
print(out) # [2, 12, 0, 1]

Related

Getting most common element from list in Python

I was able to use this piece of code to find the most common value if there was only one, however, it wouldn't work if there were multiple. I want it so that if there are multiple, it would just return None.
numbers = [5, 3, 5, 3, 2, 6, 7]
my_dict = {}
for i in numbers:
if i in my_dict:
my_dict[i] += 1
else:
my_dict[i] = 1
print(max(my_dict, key=my_dict.get))
You can use the key to get the number of occurrences of the highest value.
numbers = [5, 3, 5, 3, 2, 6, 7]
my_dict = {}
for i in numbers:
if i in my_dict:
my_dict[i] += 1
else:
my_dict[i] = 1
#print(max(my_dict, key=my_dict.get))
mx=max(my_dict, key=my_dict.get)
mn=my_dict[mx]
if mn == 1:
print(mx, ' is the highest')
else:
print('there are ',mn, ' of ',mx)
Output
there are 2 of 5
Use a library function to sort the numbers:
s = sorted(numbers)
Check if the last two numbers are the same (then you have more than one max):
one_max = s[-1] if (len(s)==1 or s[-1]!=s[-2]) else None
#ada7ke This will print None if there is more than 1 highest number. Otherwise it will print the number
numbers = [5, 7, 5, 3, 2, 6, 7]
highest = max(numbers)
indices = [i for i, x in enumerate(numbers) if x == highest]
frequency = numbers.count(highest)
if frequency > 1:
print(None)
else:
print(highest)

Count cumulatively the occurrences of each element of a list

I have a list:
selection_list=[3, 2, 3, 2, 2, 2]
I want to count cumulatively the occurrences of each element, I want the output to be:
['1/2', '1/4' , '2/2', '2/4', '3/4', '4/4'] # 1/2 means: first occurrence of two
dico={'selection':[], 'Number':[]}
l=[]
keys=[]
for i in range(len(selection_list)):
dico['selection'].append(selection_list[i])
#counting the total occurrences of each element
occurrences = collections.Counter(selection_list)
for i in occurrences.keys():
keys.append(i)
#indexes of each element of the list
for j in range(len(keys)):
l.append([i for i,val in enumerate(selection_list) if val==keys[j]])
for i in range(len(l)):
for j in l[i] :
dico['Number'].insert(int(j), str(len(l[i]))+'-'+ str(len(l[i])) )
I'm getting this output:
dico={'selection': [2, 3, 2, 3, 3, 3], 'UnfoldingNumber': ['2-2', '4-4', '2-2', '4-4', '4-4', '4-4']}
what am I missing?
This is one example for a straightforward solution:
from collections import Counter
selection_list=[3, 2, 3, 2, 2, 2]
numerator = {i:0 for i in set(selection_list)}
denominator = Counter(selection_list)
result = []
for v in selection_list:
numerator[v] += 1
result.append(str(numerator[v]) + '/' + str(denominator[v]))
print(result)

How to remove combinations that contains same number twice from the list, which contains that same number repetitively?

I want to type the code for the combinations of up-to k sub-sequences which starts from 1 to k. Here I am trying to find the sub-sequences which doesn't contain same number twice, even though list contains.
I am trying to do it, but I am failing on the first part.
import itertools
lst1=[1, 2, 3, 5, 6, 3, 9, 5, 1, 2]
k = int(input())
lst = [i for j in range(1,k+1) for i in itertools.combinations(lst1, j)]
set1 = set(lst)
print(lst)
print(set1)
The code should give answers like :
[(1,2,3), (1,3,5),...so on] but it should not give (1,2,1) or (1,2,2) because it contains repetitive number.
According to your logic i have write this code in order to remove duplicate list from list.
import itertools
lst1=[1, 2, 3, 5, 6, 3, 9, 5, 1, 2]
k = int(input())
lst = [i for j in range(1,k+1) for i in itertools.combinations(lst1, j)]
set1 = set(lst)
print(lst)
print(set1)
all_combination = list(set1)
new = []
status = False
for i in all_combination:
for j in range(len(i)):
l = j+1
for k in range(l, len(i)):
if i[j] == i[k] and i[j] not in new:
status = True
break
else:
status = False
if status == True:
break
if status == False:
new.append(i)
print(new)
But your sample answers shows the combination of given number of subsets having length 3 or n. and remove duplicate from it. This is my code which will return all the combination of numbers present in list with length 3 or n and remove duplicates, just change the value of s and you will get the combination accordingly.
l1 = [1, 2, 3, 5, 6, 3, 9, 5, 1, 2]
s = 3
new_list = []
all_combination = []
def combination(numbers, n, subset):
if len(subset) == n:
all_combination.append(subset)
else:
for i in range(len(numbers)):
combination(numbers, n, subset + [numbers[i]])
combination(l1, s, new_list)
print(all_combination)
unique = []
status = False
for i in all_combination:
for j in range(len(i)):
l = j+1
for k in range(l, len(i)):
if i[j] == i[k] and i[j]:
status = True
break
else:
status = False
if status == True:
break
if status == False:
unique.append(i)
print(unique)

Algorithm for fast combinations

Input:Given items=[1,2,3] and values=[100,300,800] OR it can be in dictionary={1:100,2:300,3:800}.
Find all combinations items such that sum values is less than 500
Solution:
[1]
[2]
[1,2]
This has to be done for millions of inputs.
WHat is the best and fastest algorithm to implement this??
import copy
dictionary = {
100: 1,
200: 2,
800: 3,
}
value = sorted([100, 200, 800])
threshold = 500
res = []
def dfs(l, s, cur):
if s < threshold:
if len(l) > 0:
res.append(l)
else:
return
for i in range(cur + 1, len(value)):
tmp = copy.copy(l)
tmp.append(dictionary.get(value[i]))
dfs(tmp, s+value[i], i)
dfs([], 0, -1)
print res
Time complexity is O(K). K is number of correct result.
A much more efficient method is to use breadth-first-search and avoid enqueueing any further if the current item value plus the current sum already reaches the limit, so that in a value list of [1, 2, 3, 4, 5] and a limit of 5, if the current combination of values is [1, 2] and the current item value is 3, then since we find that 1 + 2 + 3 is already no less than 5, we will not enqueue [1, 2, 3] for further search. This drastically cuts down on the number of combinations we need to test:
from collections import deque
def sums_less_than(items, values, limit):
seeds = [(index, 0, [], item_value) for index, item_value in enumerate(zip(items, values))]
queue = deque(seeds)
while queue:
index, _sum, combination, (item, value) = queue.popleft()
new_sum = _sum + value
if new_sum < limit:
new_combination = combination + [item]
yield new_combination
for i in range(index + 1, len(seeds)):
queue.append((i, new_sum, new_combination, seeds[i][-1]))
so that:
items=[1,2,3]
values=[100,300,800]
print(list(sums_less_than(items, values, 500)))
will output:
[[1], [2], [1, 2]]
You can use itertools.combinations on a zipped sequence of items and values after filtering out values that are greater than the limit first:
from itertools import combinations
items=[1,2,3]
values=[100,300,800]
def sums_less_than(items, values, limit):
filtered = [(item, value) for item, value in zip(items, values) if value < limit]
return [[item for item, _ in c] for n in range(1, len(filtered) + 1) for c in combinations(filtered, n) if sum(value for _, value in c) < limit]
print(sums_less_than(items, values, 500))
This outputs:
[[1], [2], [1, 2]]
With modificaton:
import copy
dictionary = {
100: 1,
200: 2,
800: 3,
50 : 4,
}
value = sorted(dictionary.keys())
threshold = 500
res = []
thres_val=[]
def dfs(l, s, cur):
if s < threshold:
if len(l) > 0:
res.append(l)
thres_val.append(s)
else:
return
for i in range(cur + 1, len(value)):
tmp = copy.copy(l)
tmp.append(dictionary.get(value[i]))
dfs(tmp, s+value[i], i)
dfs([], 0, -1)
print(res)
print(thres_val)
print("\tItem list-->Value")
j=0
for i in res:
print("\t",i,"-->",thres_val[j])`

Finding longest run in a list

Given a list of data, I'm trying to create a new list in which the value at position i is the length of the longest run starting from position i in the original list. For instance, given
x_list = [1, 1, 2, 3, 3, 3]
Should return:
run_list = [2, 1, 1, 3, 2, 1]
My solution:
freq_list = []
current = x_list[0]
count = 0
for num in x_list:
if num == current:
count += 1
else:
freq_list.append((current,count))
current = num
count = 1
freq_list.append((current,count))
run_list = []
for i in freq_list:
z = i[1]
while z > 0:
run_list.append(z)
z -= 1
Firstly I create a list freq_list of tuples, where every tuple's first element is the element from x_list, and where the second element is the number of the total run.
In this case:
freq_list = [(1, 2), (2, 1), (3, 3)]
Having this, I create a new list and append appropriate values.
However, I was wondering if there is a shorter way/another way to do this?
Here's a simple solution that iterates over the list backwards and increments a counter each time a number is repeated:
last_num = None
result = []
for num in reversed(x_list):
if num != last_num:
# if the number changed, reset the counter to 1
counter = 1
last_num = num
else:
# if the number is the same, increment the counter
counter += 1
result.append(counter)
# reverse the result
result = list(reversed(result))
Result:
[2, 1, 1, 3, 2, 1]
This is possible using itertools:
from itertools import groupby, chain
x_list = [1, 1, 2, 3, 3, 3]
gen = (range(len(list(j)), 0, -1) for _, j in groupby(x_list))
res = list(chain.from_iterable(gen))
Result
[2, 1, 1, 3, 2, 1]
Explanation
First use itertools.groupby to group identical items in your list.
For each item in your groupby, create a range object which counts backwards from the length of the number of consecutive items to 1.
Turn this all into a generator to avoid building a list of lists.
Use itertools.chain to chain the ranges from the generator.
Performance note
Performance will be inferior to #Aran-Fey's solution. Although itertools.groupby is O(n), it makes heavy use of expensive __next__ calls. These do not scale as well as iteration in simple for loops. See itertools docs for groupby pseudo-code.
If performance is your main concern, stick with the for loop.
You are performing a reverse cumulative count on contiguous groups. We can create a Numpy cumulative count function with
import numpy as np
def cumcount(a):
a = np.asarray(a)
b = np.append(False, a[:-1] != a[1:])
c = b.cumsum()
r = np.arange(len(a))
return r - np.append(0, np.flatnonzero(b))[c] + 1
and then generate our result with
a = np.array(x_list)
cumcount(a[::-1])[::-1]
array([2, 1, 1, 3, 2, 1])
I would use a generator for this kind of task because it avoids building the resulting list incrementally and can be used lazily if one wanted:
def gen(iterable): # you have to think about a better name :-)
iterable = iter(iterable)
# Get the first element, in case that fails
# we can stop right now.
try:
last_seen = next(iterable)
except StopIteration:
return
count = 1
# Go through the remaining items
for item in iterable:
if item == last_seen:
count += 1
else:
# The consecutive run finished, return the
# desired values for the run and then reset
# counter and the new item for the next run.
yield from range(count, 0, -1)
count = 1
last_seen = item
# Return the result for the last run
yield from range(count, 0, -1)
This will also work if the input cannot be reversed (certain generators/iterators cannot be reversed):
>>> x_list = (i for i in range(10)) # it's a generator despite the variable name :-)
>>> ... arans solution ...
TypeError: 'generator' object is not reversible
>>> list(gen((i for i in range(10))))
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
And it works for your input:
>>> x_list = [1, 1, 2, 3, 3, 3]
>>> list(gen(x_list))
[2, 1, 1, 3, 2, 1]
This can actually be made simpler by using itertools.groupby:
import itertools
def gen(iterable):
for _, group in itertools.groupby(iterable):
length = sum(1 for _ in group) # or len(list(group))
yield from range(length, 0, -1)
>>> x_list = [1, 1, 2, 3, 3, 3]
>>> list(gen(x_list))
[2, 1, 1, 3, 2, 1]
I also did some benchmarks and according to these Aran-Feys solution is the fastest except for long lists where piRSquareds solution wins:
This was my benchmarking setup if you want to confirm the results:
from itertools import groupby, chain
import numpy as np
def gen1(iterable):
iterable = iter(iterable)
try:
last_seen = next(iterable)
except StopIteration:
return
count = 1
for item in iterable:
if item == last_seen:
count += 1
else:
yield from range(count, 0, -1)
count = 1
last_seen = item
yield from range(count, 0, -1)
def gen2(iterable):
for _, group in groupby(iterable):
length = sum(1 for _ in group)
yield from range(length, 0, -1)
def mseifert1(iterable):
return list(gen1(iterable))
def mseifert2(iterable):
return list(gen2(iterable))
def aran(x_list):
last_num = None
result = []
for num in reversed(x_list):
if num != last_num:
counter = 1
last_num = num
else:
counter += 1
result.append(counter)
return list(reversed(result))
def jpp(x_list):
gen = (range(len(list(j)), 0, -1) for _, j in groupby(x_list))
res = list(chain.from_iterable(gen))
return res
def cumcount(a):
a = np.asarray(a)
b = np.append(False, a[:-1] != a[1:])
c = b.cumsum()
r = np.arange(len(a))
return r - np.append(0, np.flatnonzero(b))[c] + 1
def pirsquared(x_list):
a = np.array(x_list)
return cumcount(a[::-1])[::-1]
from simple_benchmark import benchmark
import random
funcs = [mseifert1, mseifert2, aran, jpp, pirsquared]
args = {2**i: [random.randint(0, 5) for _ in range(2**i)] for i in range(1, 20)}
bench = benchmark(funcs, args, "list size")
%matplotlib notebook
bench.plot()
Python 3.6.5, NumPy 1.14
Here's a simple iterative approach to achieve it using collections.Counter:
from collections import Counter
x_list = [1, 1, 2, 3, 3, 3]
x_counter, run_list = Counter(x_list), []
for x in x_list:
run_list.append(x_counter[x])
x_counter[x] -= 1
which will return you run_list as:
[2, 1, 1, 3, 2, 1]
As an alternative, here's one-liner to achieve this using list comprehension with enumerate but it is not performance efficient due to iterative usage of list.index(..):
>>> [x_list[i:].count(x) for i, x in enumerate(x_list)]
[2, 1, 1, 3, 2, 1]
You can count the consecutive equal items and then add a countdown from count-of-items to 1 to the result:
def runs(p):
old = p[0]
n = 0
q = []
for x in p:
if x == old:
n += 1
else:
q.extend(range(n, 0, -1))
n = 1
old = x
q.extend(range(n, 0, -1))
return q
(A couple of minutes later) Oh, that's the same as MSeifert's code but without the iterable aspect. This version seems to be almost as fast as the method shown by Aran-Fey.

Categories

Resources