# Code to generate the toy example (let us not worry how this code works)
nums = np.arange(1000, 6000, 1000) \
    + np.round(np.random.RandomState(0).normal(0., 200., size=5,)).astype(np.int)
df = pd.DataFrame(dict(Numbers=nums, meanX=np.power(nums, 0.5)/5., 
                       stdX=np.power(nums, 0.1)))

df

"""
Explanation: Let us suppose that we have a dataframe with 5 rows. Each row gives us the values of a quantities in each of 5 bins. So, the columns are numbers, means of some quantity X and standard deviations of X. We will assume that in each of the bins, X is drawn from a Normal distribution, and so characterizing the mean and the standard deviation in each bin characterizes this distribution.
Q: How do we create a set of random samples of X so, that in each bin the values are drawn from the appropriate Normal distribution as described above, but the total number of values is an input Nobjs.
We will generate a toy table for this first:
End of explanation
"""


df['frequencies'] = df.Numbers / df.Numbers.sum()

df

"""
Explanation: Step 1
To answer the question: we will assume that the numbers in each bin relative to the total 
    stay the same when we change the total numbers. So first :
End of explanation
"""


numObjectsPerBin = np.round(df.frequencies * 50000).astype(np.int)
print(numObjectsPerBin)

"""
Explanation: Step 2
Now, given a total number of objects Nobj, we can find approimately how many we expect in each bin = Nobj * frequencies (but that might be a fraction, so we will round it to an integer). Let us try Nobj = 50000
End of explanation
"""


np.round(df.frequencies * df.Numbers.sum()).astype(np.int)

"""
Explanation: We can check (as it obviously must) that this matches our numbers if Nobj equals the total number of objects in our 
toy example:
End of explanation
"""


m, s  = df.ix[0, ['meanX', 'stdX']] # Now the mean of the 0th bin is assigned to m, and std to s
X = np.random.normal(m, s, size=numObjectsPerBin.ix[0] )
print(X)

"""
Explanation: Step 3 Now for each bin we mgith want to draw numbers from a normal distribution with size = number of objects in that bin
For each bin this is now easy: (some syntax about how to access the ith row and two columns by name from a dataframe is necessary, but fairly intuitive)
End of explanation
"""


XVals = []
for i in range(len(df)):
    m, s  = df.ix[i, ['meanX', 'stdX']]
    # We will convert the numpy array to list, but that may not be necessary
    X = np.random.normal(m, s, size=numObjectsPerBin.ix[i]).tolist()
    XVals.append(X)


XVals

"""
Explanation: So, what we need to do is loop through the bins, and keep appending X to some list
End of explanation
"""


np.array(map(len, XVals)) / np.float(sum(map(len, XVals)))

totalobjs = sum(map(len, XVals))

"""
Explanation: XVals is a list of lists. The 0th element of XVals is a list which has all of the Xs sampled 
in the 0th bin and likewise.
We can check the frequencies match up
map is a useful function to know about (but not essential, you can use for loops or preferably us list comprehensions in its place). But here is what it does:
    sequence = [a, b, c] 
    map( func, sequence) = list(func(a), func(b), func(c)) 
So we can use this to find the frequencies and also check that the total adds up to Nobj (approximately)
End of explanation
"""


map(np.mean, XVals)

map(np.std, XVals)

"""
Explanation: And we can find their means and std deviations
End of explanation
"""


seed = 1
rng = np.random.RandomState(seed)
rng.normal(0, 1, size= 50)

"""
Explanation: Randomness and Reproducibility
There was a question about getting different values from each random draw. As guessed, this is expected, but there are times when you want to get the same answer to be able to reproduce older calculations. One of the ways to achieve this is by supplying a seed
End of explanation
"""


rng.normal(0, 1, size=50)

# But if you want to reproduce the first 50, you can do so by using the same seed
rng = np.random.RandomState(seed)
rng.normal(0,1, size=60)

"""
Explanation: The next 50 will be different
End of explanation
"""


