Assembling edges of a graph I'm making a neural network that comprises five populations of feature-selective neurons and one population of non-selective neurons.  Each neuron in this network receives connections from


c * f * N_E randomly selected neurons from each of the selective populations
c * (1 - f * p) * N_E randomly selected non-selective neurons.


Below is the code I've written to define all of the connections in this network.  Specifically, the code produces two arrays that together represent the source (ee_i) and target (ee_j) nodes of every connection.

Can anyone think of a way to reorganize this code to speed it up?

import numpy as np

N_E = 8000
p = 5
f = 0.1
c = 0.2
encoders = np.random.choice(np.arange(N_E), (p, int(f * N_E)), False)
non_selec = np.array([i for i in range(N_E) if i not in encoders])
ee_i = []
ee_j = np.repeat(np.arange(N_E), int(c * N_E))
for j in range(N_E):
    ee_i = np.concatenate([ee_i,
                           np.concatenate([np.random.choice(encoders[0, :],
                                                            int(c * f *
                                                                N_E),
                                                            False),
                                           np.random.choice(encoders[1, :],
                                                            int(c * f *
                                                                N_E),
                                                            False),
                                           np.random.choice(encoders[2, :],
                                                            int(c * f *
                                                                N_E),
                                                            False),
                                           np.random.choice(encoders[3, :],
                                                            int(c * f *
                                                                N_E),
                                                            False),
                                           np.random.choice(encoders[4, :],
                                                            int(c * f *
                                                                N_E),
                                                            False),
                                           np.random.choice(non_selec,
                                                            int(c * (1 - f *
                                                                     p) *
                                                                N_E),
                                                            False)])])
as far as I could see by googling np.concatenate is the fastest way for concatenate arrays, so I'd keep that.

but talking about code organization, I suggest you to create a function (something like it. it may have syntax errors, so that's just to give you an idea) 

def randomChoice(value, select):
    if select:
        return np.random.choice(encoders[value, :], int(c * f * N_E), False);
    else:
        return np.random.choice(non_selec, int(c * (1 - f * p) * N_E), False);


and have a simple and single call instead of a huge block of code into one line with two concatenate calls

ee_i = np.concatenate([ee_i, randomChoice(0, true), randomChoice(1, true), randomChoice(2, true), randomChoice(3, true), randomChoice(4, true), randomChoice(0, false)])


I hope this helps