投资组合权重的所有可能组合
我在寻找一些技巧和窍门,想要更好地用Python编写以下代码(比如,去掉多余的循环和复制,使用更多的切片)
我写了这个代码来创建一个N个证券组合的所有可能的权重组合,并且要满足以下条件:
权重来自一个可能的列表(在这个例子中是0, 0.1, 0.2, 0.3, 0.4, 0.5)
一个有效的组合的权重总和必须等于1(也就是完全投资)
这种方法并不可行,因为组合的数量很快就会变得难以管理。这只是我在努力掌握这门语言。
非常感谢大家的帮助!
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)
2 个回答
1
你可以使用 itertools.combinations()
这个工具,不过你需要逐步增加组合的大小,直到达到数据集的长度。
>>> 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)]
仔细阅读你的需求,更新组合的条件为 ==1
,而不是 <= 1
。
注意 -- 如果你的输入数据集非常大,你可能需要一个更好的算法,因为这个方法是暴力破解的。
2
你应该使用itertools模块,因为里面已经有很多现成的算法,可以帮你完成大部分想做的事情。
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)]
如果你只关心权重的独特组合(顺序不重要),那么你需要用到combinations
;如果顺序很重要,那就应该用permutations
,可以这样使用:
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 ...