Home > OS >  How can I take a 1D numpy array of length N, and replace the first 5 elements with values less than
How can I take a 1D numpy array of length N, and replace the first 5 elements with values less than

Time:08-19

I'm working on a portfolio optimisation problem including cardinality and max weighting constraints, using a genetic algorithm to generate solutions. I want to create arrays for the initial population that meet a cardinality constraint of 5 (5 non zero elements total) and each of those numbers is less than 0.25, and those 5 elements sum to 1.

population_size = 100
cardinality_constraint = 5
# 50 is the length of the vector of assets that we can allocate to

initial_pop = np.zeros((population_size, 50)))

for i in range(population_size):
    initial_pop[i,:cardinality_constraint] = np.random.rand(cardinality_constraint)
    initial_pop[i] = initial_pop[i] / np.sum(initial_pop[i])
    np.random.shuffle(initial_pop[i])

This creates an array that satisfies the cardinality constraint, putting values into the first 5 elements, normalising them, and then shuffling them.

I've been trying to think of a way to incorpate the max weighting constraint. I think all I need to do is find a way to randomly generate 5 numbers that are each less then 0.25, and together sum to 1. I'm strugging with that though, because I can easily create 5 random numbers in that range, but if I then normalise some of the values might then exceed the maximum.

Any ideas would be most appreciated thanks!

FOLLOW UP:

I think I have found a solution that I've written up which uses the numpy dirichlet function.

So as the example I originally described, where I want to have a cardinality constraint of 5 and a maximum individual asset weight of 0.25, or 25% of the portfolio:


initial_population_list = []

while len(initial_population_list) < 100:
    x = np.random.dirichlet(np.ones(5), size = 1)
    if len(x[x > 0.25]) < 1:
        initial_population_list.append(x)

Seems to me to work well! Thanks for the suggestion to look at that function.

CodePudding user response:

I'm not aware of anything in Numpy that would help with this, but you could solve this relatively easily using the standard library:

from random import uniform, shuffle

def constrained_simplex(count, *, max_val):
    remain = 1
    result = []
    for i in range(count - 1, 0, -1):
        val = uniform(
            max(0, remain - i * max_val),
            min(max_val, remain),
        )
        result.append(val)
        remain -= val
    assert 0 <= remain <= max_val
    result.append(remain)
    # shuffle(result)
    return result

vals = constrained_simplex(5, max_val=0.25)

which would give you something like: 0.022 0.245 0.241 0.244 0.248 back.

A problem with this is that earlier elements are more likely to be smaller than later elements. Uncommenting the shuffle(result) line would perturb the index locations appropriately. Not sure if this is an issue for your application!

CodePudding user response:

I assume you do not have a problem using the numbers once you have generated the correct one.

I assume I can focus on the problem of generation.

I also assume you are interested in generating strictly positive numbers.

Let's start with a function to compute if a certain array satisfy the constraints:

  • all values below max_
  • all values below min_
  • the sum of all values close to total (just close and not equal because of hist_uniform hist_normal

  • Related