I wanted to share my solution, should anyone else run into this issue and not find any publicly available solutions.
## Dependencies ----
import numpy as np
import random
## Conveniently modifiable variables ----
# Completely arbitrary; just choosing values to demonstate
seed = 101
ISI_n = 100
ISI_mean = 2.000
ISI_lower = 0.500
ISI_upper = 4.000
## Function -----
def generate_jitter(seed, ISI_n, ISI_mean, ISI_lower, ISI_upper):
# A tracker to track which iteration we are currently on
tracker = 1
# A seed to be able to create the same simulation reliably
random.seed(seed)
# Creating an array to house the jitter values
jitters = []
# Iterate through this loop ISI_n times
while tracker <= ISI_n:
# If this is the first iteration
if tracker == 1:
# Add a random value with 3 digits that's between ISI_lower and ISI_upper to the array.
jitters.append(round(random.uniform(ISI_lower, ISI_upper), 3))
# If this is any other iteration
if tracker > 1:
# Calculate the value that would make the mean of array Jitters equal to ISI_mean
balance = (ISI_mean * tracker) - sum(jitters)
# Calculate how many iterations are left in this loop
progress = ((ISI_n - tracker)/ISI_n)
# Calculate an upper bound centered around the balance value, but not the balance value.
# As the loop grows closer to the final iteration, this value will be closer to the balance value.
upper = balance + ((ISI_upper - balance) * progress)
# If on the off chance upper is greater than ISI_upper
if upper > ISI_upper:
# Make upper equivalent to ISI_upper
upper = ISI_upper
lower = balance - ((balance - ISI_lower) * progress)
# If on the off chance upper is greater than ISI_upper
if lower < ISI_lower:
# Make upper equivalent to ISI_upper
lower = ISI_lower
# Then generate a new float between upper and lower
jitters.append(round(random.uniform(lower, upper), 3))
# Add 1 to the tracker in preparation for the next iteration
tracker += 1
# Randomly shuffle the array
random.shuffle(jitters)
return jitters
# Demonstrating that the function works using the values defined above.
array = generate_jitter(seed = seed,
ISI_n = ISI_n,
ISI_mean = ISI_mean,
ISI_lower = ISI_lower,
ISI_upper = ISI_upper)
print(np.mean(array))
print(np.min(array))
print(np.max(array))
print(len(array))
My output:
# Slightly off, but our MRI clock won't register
# 0.0000000000000002 seconds. This will get rounded to 2.000
# so close enough for my purposes
>>> print(np.mean(array))
1.9999999999999998
>>> print(np.min(array))
0.702
>>> print(np.max(array))
3.83
>>> print(len(array))
100
>>>