Distribute values based on sum and list of provided values - python

I need to generate list of values from provided that satisfy this requirements:
Sum of all generated values should be equal of total, only providedValues should be used to get the sum, providedValues and total can be any double.
For example:
total = 1.0
providedValues = [0.5, 0.25]
Values in output list should be randomly distributed, for example output can be: [0.5, 0.25, 0.25], [0.25, 0.5, 0.25] or [0.25, 0.25, 0.5]
In case sum can't be equal total:
total = 1.0
providedValues = [0.3]
algorithm should throw error.
Language for implementation not so matter, I'll try to read any.

This algorithm will return all the possible combinations that sum to total.
import itertools
import numpy as np
def find_combination(total, providedValues):
i = 1
rv = []
while True:
combs = list(itertools.combinations_with_replacement(providedValues,i))
validCombs = [comb for comb in combs if np.isclose(sum(comb),total)]
if validCombs:
rv.extend(validCombs)
elif not [comb for comb in combs if sum(comb) <= total]:
return rv
i += 1
Output:
>>> find_combination(1.0, [0.5, 0.25])
[(0.5, 0.5), (0.5, 0.25, 0.25), (0.25, 0.25, 0.25, 0.25)]
>>> find_combination(1.0, [0.3])
[]
If you want to get all permutations of the results, you can use
>>> set(itertools.permutations((0.5, 0.25, 0.25)))
{(0.25, 0.25, 0.5), (0.25, 0.5, 0.25), (0.5, 0.25, 0.25)}
For example:
>>> set(y for x in find_combination(1.0, [0.5, 0.25]) for y in itertools.permutations(x))
{(0.25, 0.25, 0.25, 0.25),
(0.25, 0.25, 0.5),
(0.25, 0.5, 0.25),
(0.5, 0.25, 0.25),
(0.5, 0.5)}

Here is my solution based on there are two values provided, you may want to change it for you need
from itertools import permutations, combinations
def get_scala(x,y,t):
# get list of scala combinations
# find a,b that a*x+b*y = total
scala_list = []
amax = int(t // x) # possible max scala for x
bmax = int(t // y) # possible max scala for y
for i in range(1, amax+1):
for j in range(1, bmax+1):
if i*x + j*y == t: # find the scala combination that == total
scala_list.append((i, j))
if scala_list:
return scala_list
else:
print("Warning: cannot add up to the total")
def dist(x, y, scala):
a, b = scala
# get a base list with a number of x and b number of y [x,x,y,y,y]
bl = [x]*a + [y]*b
# get permutations and using set to get rid of duplicate items
return set(permutations(bl))
for l in get_scala(0.3, 0.2, 1):
for d in dist(0.3, 0.2, l):
print(d)
the output would look look:
(0.2, 0.3, 0.2, 0.3)
(0.2, 0.2, 0.3, 0.3)
(0.3, 0.2, 0.2, 0.3)
(0.3, 0.2, 0.3, 0.2)
(0.3, 0.3, 0.2, 0.2)
(0.2, 0.3, 0.3, 0.2)

Related

Python display numbers without a comma in a list

below I put the code I would like to get the result like: 0.1, 0.2, 0.3, 0.4 .... but I get this result [0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9] how can I remove those zeros after the decimal point?
squares = []
for i in range(10):
squares.append(i * (0.1))
print(squares)
You can use something like this:
>>> ['{:.2}'.format(i * 0.1) for i in range(10)]
Use the str method format to specify how many decimals to display.
squares = []
for i in range(10):
squares.append(i * (0.1))
print(*["{:.1f}".format(s) for s in squares], sep=', ')
0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9
Sup, Kozinski. Hope you're having a great time.
squares = []
for i in range(10):
squares.append(round(i * (0.1), 1)) #integers will be stored in a proper format
print(squares)
Check out this round function

How to find N maximum product subarrays of M elements of a Numpy array?

I have a Numpy array, and I need to find the N maximum product subarrays of M elements. For example, I have the array p = [0.1, 0.2, 0.8, 0.5, 0.7, 0.9, 0.3, 0.5] and I want to find the 5 highest product subarrays of 3 elements. Is there a "fast" way to do that?
Here is another quick way to do it:
import numpy as np
p = [0.1, 0.2, 0.8, 0.5, 0.7, 0.9, 0.3, 0.5]
n = 5
m = 3
# Cumulative product (starting with 1)
pc = np.cumprod(np.r_[1, p])
# Cumulative product of each window
w = pc[m:] / pc[:-m]
# Indices of the first element of top N windows
idx = np.argpartition(w, n)[-n:]
print(idx)
# [1 2 5 4 3]
Approach #1
We can create sliding windows and then perform prod reduction and finally np.argpartition to get top N ones among them -
from skimage.util.shape import view_as_windows
def topN_windowed_prod(a, W, N):
w = view_as_windows(a,W)
return w[w.prod(1).argpartition(-N)[-N:]]
Sample run -
In [2]: p = np.array([0.1, 0.2, 0.8, 0.5, 0.7, 0.9, 0.3, 0.5])
In [3]: topN_windowed_prod(p, W=3, N=2)
Out[3]:
array([[0.8, 0.5, 0.7],
[0.5, 0.7, 0.9]])
Note that the order is not maintained with np.argpartition. So, if we need the top N in descending order of prod values, use range(N) with it. More info.
Approach #2
For smaller window lengths, we can simply slice and get our desired result, like so -
def topN_windowed_prod_with_slicing(a, W, N):
w = view_as_windows(a,W)
L = len(a)-W+1
acc = a[:L].copy()
for i in range(1,W):
acc *= a[i:i+L]
idx = acc.argpartition(-N)[-N:]
return w[idx]

Finding the probability of a variable in collection of lists

I have a selection of lists of variables
import numpy.random as npr
w = [0.02, 0.03, 0.05, 0.07, 0.11, 0.13, 0.17]
x = 1
y = False
z = [0.12, 0.2, 0.25, 0.05, 0.08, 0.125, 0.175]
v = npr.choice(w, x, y, z)
I want to find the probability of the value V being a selection of variables eg; False or 0.12.
How do I do this.
Heres what I've tried;
import numpy.random as npr
import math
w = [0.02, 0.03, 0.05, 0.07, 0.11, 0.13, 0.17]
x = 1
y = False
z = [0.12, 0.2, 0.25, 0.05, 0.08, 0.125, 0.175]
v = npr.choice(w, x, y, z)
from collections import Counter
c = Counter(0.02, 0.03, 0.05, 0.07, 0.11, 0.13, 0.17,1,False,0.12, 0.2, 0.25, 0.05, 0.08, 0.125, 0.175)
def probability(0.12):
return float(c[v]/len(w,x,y,z))
which I'm getting that 0.12 is an invalid syntax
There are several issues in the code, I think you want the following:
import numpy.random as npr
import math
from collections import Counter
def probability(v=0.12):
return float(c[v]/len(combined))
w = [0.02, 0.03, 0.05, 0.07, 0.11, 0.13, 0.17]
x = [1]
y = [False]
z = [0.12, 0.2, 0.25, 0.05, 0.08, 0.125, 0.175]
combined = w + x + y + z
v = npr.choice(combined)
c = Counter(combined)
print(probability())
print(probability(v=0.05))
1) def probability(0.12) does not make sense; you will have to pass a variable which can also have a default value (above I use 0.12)
2) len(w, x, y, z) does not make much sense either; you probably look for a list that combines all the elements of w, x, y and z. I put all of those in the list combined.
3) One would also have to put in an additional check, in case the user passes e.g. v=12345 which is not included in combined (I leave this to you).
The above will print
0.0625
0.125
which gives the expected outcome.

All possible combinations of portfolio weights [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
I am looking for tips and tricks to better code the following in Python (e.g. remove superfluous loops and copying, use more splicing)
I have coded this to create all possible combinations of weights for a portfolio of N securities subject to the constraints:
Weights come from a list of possibilities (in this case 0,.1,.2,.3,.4,.5)
The sum of weights for a valid portfolio must = 1 (Fully Invested)
This is not a valid approach as the number of combinations quickly becomes unmanageable. It's just me trying to get to grips with this language.
Thank you all in advance for you help!
import copy as cp
def generateWeights (weights,possibleWeights,N):
"Generate all possible combinations of weights"
# cycle over number of desired coloumns
for i in range(0,N):
# copy weights to iterate over every element while i pop stuff out of
# the original list
weightsCopy = cp.deepcopy(weights)
for w in weightsCopy:
# make a copy to edit
wtemp = cp.deepcopy(w)
for p in possibleWeights:
# append every possibility
wtemp.append(p)
# I only want combinations with sum == 1 so I can start
# avoiding those that are > 1
if sum(wtemp) <= 1:
weights.append(cp.deepcopy(wtemp))
# get the original wtemp back so I can work on it with my next p
wtemp.pop()
# finished developing the first line of the table. Pop it out and
# move on.
weights.pop(0)
# once again copy weights to iterate over every element while I edit the
# original list
weightsCopy = cp.deepcopy(weights)
for w in weightsCopy:
# remove all possibilities whose sum < 1
# all those > 1 were never added
if sum(w) < 1:
weights.remove(w)
return weights
N=6 # Number of securities
possibleWeights = [0.0,0.1,0.2,0.3,0.4,0.5]
# weights is a coloumn because I want to access its elements and still get
# lists instead of floats.
weights = [[0.0],[0.1],[0.2],[0.3],[0.4],[0.5]]
weights = generateWeights(weights,possibleWeights,N)
You should use the itertools module that already has the algorithms to do most of what you want.
from itertools import combinations
def valid_combinations(weights):
'''generator of possible combinations of weights elements that add up to 1'''
list_length = len(weights) # we will need this
for lengths in range(list_length):
for possible in combinations(weights, lengths): # all possible orderings of weights
if sum(possible[:lengths]) == 1: # only generate valid ones
yield possible[:lengths]
>>> original = [0, .1, .2, .3, .4, .5]
>>> print list(valid_combinations(original))
[(0.1, 0.4, 0.5), (0.2, 0.3, 0.5), (0, 0.1, 0.4, 0.5), (0, 0.2, 0.3, 0.5), (0.1, 0.2, 0.3, 0.4), (0, 0.1, 0.2, 0.3, 0.4)]
If you are only interested in unique combinations of the weights (the order does not matter), you need to use combinations, if it does matter you should use permutations the following way:
from itertools import permutations
def valid_combinations(weights):
'''generator of possible combinations of weights elements that add up to 1'''
list_length = len(weights) # we will need this
for possible in permutations(weights): # all possible orderings of weights
for lengths in range(list_length): # get all prefix sublists
if sum(possible[:lengths]) == 1: # only generate valid ones
yield possible[:lengths]
>>> original = [0, .1, .2, .3, .4, .5]
>>> print list(valid_combinations(original))
>>> [(0, 0.1, 0.2, 0.3, 0.4), (0, 0.1, 0.2, 0.4, 0.3), (0, 0.1, 0.3, 0.2, 0.4), (0, 0.1, 0.3, 0.4, 0.2), (0, 0.1, 0.4, 0.2, 0.3), (0, 0.1, 0.4, 0.3, 0.2), (0, 0.1, 0.4, 0.5), (0, 0.1, 0.4, 0.5), (0, 0.1, 0.5, 0.4), (0, 0.1, 0.5, 0.4), (0, 0.2, 0.1, 0.3, 0.4), (0, 0.2 ...
You can use itertools.combinations(), however you will have to increase the size of combinations until you reach the length of the dataset.
>>> input_list = [0,.1,.2,.3,.4,.5]
>>> from itertools import combinations
>>> valid_combinations = []
>>> for comb_length in range(1,len(input_list)+1):
possible_combinations = combinations(input_list,comb_length)
for comb in possible_combinations:
if sum(comb) ==1:
valid_combinations.append(comb)
>>>valid_combinations
[(0.1, 0.4, 0.5), (0.2, 0.3, 0.5), (0, 0.1, 0.4, 0.5), (0, 0.2, 0.3, 0.5), (0.1, 0.2, 0.3, 0.4), (0, 0.1, 0.2, 0.3, 0.4)]
Read your requirements and updated to have combinations ==1, not <= 1.
Note -- if your dataset for input is very large, you will need a better algorithm as this is bruteforce.

Python sublist for a condition

I have 3 lists x, y, z and I plot them with:
ax.plot3D(x, y, z, linestyle = 'None', marker = 'o').
What is the easiest way to only plot the points where x > 0.5?
(my problem is how to define a sublist under a condition without making a for loop on that list).
I'm not sure why you're avoiding looping over a list and I'm assuming that you want the related points in the other lists also removing.
>>> x = [0.0, 0.4, 0.6, 1.0]
>>> y = [0.0, 2.2, 1.5, 1.6]
>>> z = [0.0, 9.1, 1.0, 0.9]
>>> zip(x,y,z)
[(0.0, 0.0, 0.0), (0.4, 2.2, 9.1), (0.6, 1.5, 1.0), (1.0, 1.6, 0.9)]
>>> [item for item in zip(x,y,z) if item[0] > 0.5]
[(0.6, 1.5, 1.0), (1.0, 1.6, 0.9)]
Separating the list into it's constituent lists will require looping over the list somehow.
It's impossible to verify a condition on every element of a list without iterating over it at least once. You could use numpy here for easy access to the elements after condition is passsed and do:
import numpy
x = [0.0, 0.4, 0.6, 1.0]
y = [0.0, 2.2, 1.5, 1.6]
z = [0.0, 9.1, 1.0, 0.9]
res = numpy.array([[x[i], y[i], z[i]] for i in xrange(len(x)) if x[i] > 0.5])
ax.plot3D(res[:,0], res[:,1], res[:,2], linestyle="None, marker='o'")
A simple list comprehension won't be enough to remove the (x,y,z) tuples if x <= 0.5, you'll have to do a little more, I use operator.itemgetter for the second part :
from operator import itemgetter
result = [(a, b, c) for a,b,c in zip(x,y,z) if a > 0.5] # first, remove the triplet
x = itemgetter(0)(result) # then grab from the new list the x,y,z parts
y = itemgetter(1)(result)
z = itemgetter(2)(result)
ax.plot3D(x, y, z, linestyle="None, marker='o')
EDIT:
Following and upgrading #shenshei advice we can achieve it with a one-line:
ax.plot3D(
*zip(*[(a, b, c) for a,b,c in zip(x,y,z) if a > 0.5]),
linestyle="None,
marker='o'
)
Reposting my comment as an answer as suggested by #StephenPaulger . You can do this with a generator expression and a couple of calls to the built-in zip():
x = [0.0, 0.4, 0.6, 1.0]
y = [0.0, 2.2, 1.5, 1.6]
z = [0.0, 9.1, 1.0, 0.9]
points = (point for point in zip(x, y, z) if point[0] > 0.5)
x, y, z = zip(*points)
You could also use a list comprehension for points if you want to, but - assuming Python 3, where zip() no longer precomputes a full list when called - that might hurt your memory usage and speed, especially if the number of points is large.
Probably using numpy would provide the cleanest approach. However, you will need to have lists/arrays x, y, and z as numpy arrays. So, first convert these lists to numpy arrays:
import numpy as np
x = np.asarray(x)
y = np.asarray(y)
z = np.asarray(z)
Now compute an array of indices of elements that satisfy your condition:
idx = np.where(x > 0.5)
NOTE: Alternatively, you could compute a boolean mask: idx=x>0.5 (this will not change the use of idx in the next ax.plot3D statement).
Use these indices to select only those specific points in x, y, and z that satisfy desired condition:
ax.plot3D(x[idx], y[idx], z[idx], linestyle = 'None', marker = 'o')
I don't want to steal lvc's thunder, but here's a variant on their answer:
>>> x = [0.1, 0.6, 0.2, 0.8, 0.9]
>>> y = [0.3, 0.1, 0.9, 0.5, 0.8]
>>> z = [0.9, 0.2, 0.7, 0.4, 0.3]
>>>
>>> a, b, c = zip(*filter(lambda t: t[0] > 0.5, zip(x, y, z)))
>>> print a, "\n", b, "\n", c
(0.6, 0.8, 0.9)
(0.1, 0.5, 0.8)
(0.2, 0.4, 0.3)
>>> ax.plot3D(a, b, c, linestyle = 'None', marker = 'o')

Categories

Resources