Python:获取投资组合的所有可能的权重组合
Python: get every possible combination of weights for a portfolio
我认为这个问题可以使用 itertools 或 cartesian 来解决,但我是 Python 的新手并且正在努力使用这些:
我有一个包含 5 只股票的投资组合,其中每只股票的权重可以为 -0.4、-0.2、0、0.2 或 0.4,权重加起来为 0。如何创建生成列表的函数各种可能的权重组合。例如[-0.4, 0.2, 0, 0.2, 0]...等
理想情况下,该函数适用于 n 只股票,因为我最终想对 50 只股票执行相同的过程。
编辑:为了澄清,我正在寻找长度为 n(在本例中为 5)的所有组合,总和为 0。值可以重复:例如:[0.2 , 0.2, -0.4, 0, 0], [ 0.4, 0, -0.2, -0.2, 0.4], [0,0,0,0.2,-0.2], [0, 0.4, -0.4, 0.2, -0.2 ] 等。所以 [0,0,0,0,0] 将是一个可能的组合。事实上,有 5 种可能的权重和 5 只股票是巧合(我应该避免!),同样的问题可能有 5 种可能的权重和 3 只股票或 7 只股票。谢谢
这是您要找的吗:
如果 L = [-0.4, 0.2, 0, 0.2, 0]
AllCombi = itertools.permutations(L)
for each in AllCombi:
print each
如果您只想要所有组合的列表,请使用 itertools.combinations
:
w = [-0.4, -0.2, 0, 0.2, 0.4]
l = len(w)
if __name__ == '__main__':
for i in xrange(1, l+1):
for p in itertools.combinations(w, i):
print p
如果要统计这些组合可以产生的不同权重,那就有点复杂了。
首先,您生成具有 1、2、3 ... 元素的排列。然后你把它们相加。然后将总和添加到集合中(如果数字已经存在,则不会执行任何操作,操作速度非常快)。最后,您转换为列表并对其进行排序。
from itertools import combinations
def round_it(n, p):
"""rounds n, to have maximum p figures to the right of the comma"""
return int((10**p)*n)/float(10**p)
w = [-0.4, -0.2, 0, 0.2, 0.4]
l = len(w)
res = set()
if __name__ == '__main__':
for i in xrange(1, l+1):
for p in combinations(w, i):
res.add(round_it(sum(p), 10)) # rounding necessary to avoid artifacts
print sorted(list(res))
我喜欢使用 filter
功能:
from itertools import permutations
w = [-0.4, -0.2, 0, 0.2, 0.4]
def foo(w):
perms = list(permutations(w))
sum0 = filter(lambda x: sum(x)==0, perms)
return sum0
print foo(w)
我知道这些值可以重复,但总和必须为零,因此解决方案可能是:
>>> from itertools import permutations
>>> weights = [-0.4, -0.2, 0, 0.2, 0.4]
>>> result = (com for com in permutations(weights) if sum(com)==0)
>>> for i in result: print(i)
编辑:
您可以按照@Steve Jassop 的建议使用 product
。
combi = (i for i in itertools.product(weights, repeat= len(weights)) if not sum(i))
for c in combi:
print(c)
类似这样,虽然效率不高。
from decimal import Decimal
import itertools
# possible optimization: use integers rather than Decimal
weights = [Decimal("-0.4"), Decimal("-0.2"), Decimal(0), Decimal("0.2"), Decimal("0.4")]
def possible_weightings(n = 5, target = 0):
for all_bar_one in itertools.product(weights, repeat = n - 1):
final = target - sum(all_bar_one)
if final in weights:
yield all_bar_one + (final,)
我从评论中重复一遍,你不能为n = 50
这样做。代码产生正确的值,但宇宙中没有时间迭代所有可能的权重。
这段代码并不出色。它会做一些不必要的工作来检查情况,例如,除了前两个之外所有的总和已经大于 0.8,因此没有必要单独检查这两个中第一个的所有可能性。
所以,这 n = 5
几乎没有时间,但有一些 n
的价值,这段代码变得非常慢,你可以用更好的代码走得更远。你仍然不会达到 50。我懒得写更好的代码,但基本上你可以用 n
的较小值递归调用 possible_weightings
而不是 all_bar_one
target
的值等于给定的目标减去目前的总和。然后修剪所有不需要的分支,在 target
太大(正或负)而无法仅使用 n
值的情况下尽早退出。
不同的方法。
1 按顺序找出所有加起来为零的权重序列。
例如,这些是一些可能性(使用整数来减少输入):
[0, 0, 0, 0, 0]
[-4, 0, 0, +2, +2]
[-4, 0, 0, 0, +4]
[-4, +4, 0, 0, 0] 不正确,因为权重未按顺序选取。
2 排列你上面得到的,因为排列加起来也是零。
这是您获得 [-4, 0, 0, 0, +4] 和 [-4, +4, 0, 0, 0] 的地方
好吧,偷懒了。我将 pseudo-code/comment-code 大量使用我的解决方案。递归不强,这些东西太棘手,无法快速编码,我怀疑这种类型的解决方案是否可以扩展到 50。
即我不认为我是对的,但它可能会给别人一个想法。
def find_solution(weights, length, last_pick, target_sum):
# returns a list of solutions, in growing order, of weights adding up to the target_sum
# weights are the sequence of possible weights - IN ORDER, NO REPEATS
# length is how many weights we are adding up
# last_pick - the weight picked by the caller
# target_sum is what we are aiming for, which will always be >=0
solutions = []
if length > 1:
#since we are picking in order, having picked 0 "disqualifies" -4 and -2.
if last_pick > weights[0]:
weights = [w for w in weights if w >= last_pick]
#all remaining weights are possible
for weight in weights:
child_target_sum = target_sum + weight
#basic idea, we are picking in growing order
#if we start out picking +2 in a [-4,-2,0,+2,+4] list in order, then we are constrained to finding -2
#with just 2 and 4 as choices. won't work.
if child_target_sum <= 0:
break
child_solutions = find_solution(weights, length=length-1, last_pick=weight, target_sum=child_target_sum)
[solutions.append([weight] + child ) for child in child_solutions if child_solution]
else:
#only 1 item to pick left, so it has be the target_sum
if target_sum in weights:
return [[target_sum]]
return solutions
weights = list(set(weights))
weights.sort()
#those are not permutated yet
solutions = find_solutions(weights, len(solution), -999999999, 0)
permutated = []
for solution in solutions:
permutated.extend(itertools.permutations(solution))
我认为这个问题可以使用 itertools 或 cartesian 来解决,但我是 Python 的新手并且正在努力使用这些:
我有一个包含 5 只股票的投资组合,其中每只股票的权重可以为 -0.4、-0.2、0、0.2 或 0.4,权重加起来为 0。如何创建生成列表的函数各种可能的权重组合。例如[-0.4, 0.2, 0, 0.2, 0]...等
理想情况下,该函数适用于 n 只股票,因为我最终想对 50 只股票执行相同的过程。
编辑:为了澄清,我正在寻找长度为 n(在本例中为 5)的所有组合,总和为 0。值可以重复:例如:[0.2 , 0.2, -0.4, 0, 0], [ 0.4, 0, -0.2, -0.2, 0.4], [0,0,0,0.2,-0.2], [0, 0.4, -0.4, 0.2, -0.2 ] 等。所以 [0,0,0,0,0] 将是一个可能的组合。事实上,有 5 种可能的权重和 5 只股票是巧合(我应该避免!),同样的问题可能有 5 种可能的权重和 3 只股票或 7 只股票。谢谢
这是您要找的吗:
如果 L = [-0.4, 0.2, 0, 0.2, 0]
AllCombi = itertools.permutations(L)
for each in AllCombi:
print each
如果您只想要所有组合的列表,请使用 itertools.combinations
:
w = [-0.4, -0.2, 0, 0.2, 0.4]
l = len(w)
if __name__ == '__main__':
for i in xrange(1, l+1):
for p in itertools.combinations(w, i):
print p
如果要统计这些组合可以产生的不同权重,那就有点复杂了。
首先,您生成具有 1、2、3 ... 元素的排列。然后你把它们相加。然后将总和添加到集合中(如果数字已经存在,则不会执行任何操作,操作速度非常快)。最后,您转换为列表并对其进行排序。
from itertools import combinations
def round_it(n, p):
"""rounds n, to have maximum p figures to the right of the comma"""
return int((10**p)*n)/float(10**p)
w = [-0.4, -0.2, 0, 0.2, 0.4]
l = len(w)
res = set()
if __name__ == '__main__':
for i in xrange(1, l+1):
for p in combinations(w, i):
res.add(round_it(sum(p), 10)) # rounding necessary to avoid artifacts
print sorted(list(res))
我喜欢使用 filter
功能:
from itertools import permutations
w = [-0.4, -0.2, 0, 0.2, 0.4]
def foo(w):
perms = list(permutations(w))
sum0 = filter(lambda x: sum(x)==0, perms)
return sum0
print foo(w)
我知道这些值可以重复,但总和必须为零,因此解决方案可能是:
>>> from itertools import permutations
>>> weights = [-0.4, -0.2, 0, 0.2, 0.4]
>>> result = (com for com in permutations(weights) if sum(com)==0)
>>> for i in result: print(i)
编辑:
您可以按照@Steve Jassop 的建议使用 product
。
combi = (i for i in itertools.product(weights, repeat= len(weights)) if not sum(i))
for c in combi:
print(c)
类似这样,虽然效率不高。
from decimal import Decimal
import itertools
# possible optimization: use integers rather than Decimal
weights = [Decimal("-0.4"), Decimal("-0.2"), Decimal(0), Decimal("0.2"), Decimal("0.4")]
def possible_weightings(n = 5, target = 0):
for all_bar_one in itertools.product(weights, repeat = n - 1):
final = target - sum(all_bar_one)
if final in weights:
yield all_bar_one + (final,)
我从评论中重复一遍,你不能为n = 50
这样做。代码产生正确的值,但宇宙中没有时间迭代所有可能的权重。
这段代码并不出色。它会做一些不必要的工作来检查情况,例如,除了前两个之外所有的总和已经大于 0.8,因此没有必要单独检查这两个中第一个的所有可能性。
所以,这 n = 5
几乎没有时间,但有一些 n
的价值,这段代码变得非常慢,你可以用更好的代码走得更远。你仍然不会达到 50。我懒得写更好的代码,但基本上你可以用 n
的较小值递归调用 possible_weightings
而不是 all_bar_one
target
的值等于给定的目标减去目前的总和。然后修剪所有不需要的分支,在 target
太大(正或负)而无法仅使用 n
值的情况下尽早退出。
不同的方法。
1 按顺序找出所有加起来为零的权重序列。
例如,这些是一些可能性(使用整数来减少输入):
[0, 0, 0, 0, 0]
[-4, 0, 0, +2, +2]
[-4, 0, 0, 0, +4]
[-4, +4, 0, 0, 0] 不正确,因为权重未按顺序选取。
2 排列你上面得到的,因为排列加起来也是零。
这是您获得 [-4, 0, 0, 0, +4] 和 [-4, +4, 0, 0, 0] 的地方
好吧,偷懒了。我将 pseudo-code/comment-code 大量使用我的解决方案。递归不强,这些东西太棘手,无法快速编码,我怀疑这种类型的解决方案是否可以扩展到 50。
即我不认为我是对的,但它可能会给别人一个想法。
def find_solution(weights, length, last_pick, target_sum):
# returns a list of solutions, in growing order, of weights adding up to the target_sum
# weights are the sequence of possible weights - IN ORDER, NO REPEATS
# length is how many weights we are adding up
# last_pick - the weight picked by the caller
# target_sum is what we are aiming for, which will always be >=0
solutions = []
if length > 1:
#since we are picking in order, having picked 0 "disqualifies" -4 and -2.
if last_pick > weights[0]:
weights = [w for w in weights if w >= last_pick]
#all remaining weights are possible
for weight in weights:
child_target_sum = target_sum + weight
#basic idea, we are picking in growing order
#if we start out picking +2 in a [-4,-2,0,+2,+4] list in order, then we are constrained to finding -2
#with just 2 and 4 as choices. won't work.
if child_target_sum <= 0:
break
child_solutions = find_solution(weights, length=length-1, last_pick=weight, target_sum=child_target_sum)
[solutions.append([weight] + child ) for child in child_solutions if child_solution]
else:
#only 1 item to pick left, so it has be the target_sum
if target_sum in weights:
return [[target_sum]]
return solutions
weights = list(set(weights))
weights.sort()
#those are not permutated yet
solutions = find_solutions(weights, len(solution), -999999999, 0)
permutated = []
for solution in solutions:
permutated.extend(itertools.permutations(solution))