<p>I am trying to automate functional testing of a server using a realistic frequency distribution of requests. (sort of load testing, sort of simulation)</p>

<p>I've chosen the <a href="http://en.wikipedia.org/wiki/Weibull_distribution" rel="nofollow noreferrer">Weibull</a> distribution as it "sort of" matches the distribution I've observed (ramps up quickly, drops off quickly but not instantly)</p>

<p>I use this distribution to generate the number of requests that should be sent each day between a given start and end date</p>

<p>I've hacked together an algorithm in Python that sort of works but it feels kludgy:</p>

<pre><code>how_many_days = (end_date - start_date).days
freqs = defaultdict(int)
for x in xrange(how_many_responses):
    freqs[int(how_many_days * weibullvariate(0.5, 2))] += 1
timeline = []
day = start_date
for i,freq in sorted(freqs.iteritems()):
    timeline.append((day, freq))
    day += timedelta(days=1)
return timeline
</code></pre>

<p>What better ways are there to do this? </p>

[{'answer_id': 53791, 'author': 'Vinko Vrsalovic', 'author_id': 5190, 'author_profile': 'https://Stackoverflow.com/users/5190', 'pm_score': 1, 'selected': False, 'text': '<p>Why don\'t you try <a href="http://grinder.sourceforge.net" rel="nofollow noreferrer">The Grinder 3</a> to load test your server, it comes with all this and more prebuilt, and it supports python as a scripting language</p>\n'}, {'answer_id': 56032, 'author': 'Kai', 'author_id': 2963, 'author_profile': 'https://Stackoverflow.com/users/2963', 'pm_score': 0, 'selected': False, 'text': "<p>Instead of giving the number of requests as a fixed value, why not use a scaling factor instead? At the moment, you're treating requests as a limited quantity, and randomising the days on which those requests fall. It would seem more reasonable to treat your requests-per-day as independent.</p>\n\n<pre><code>from datetime import *\nfrom random import *\n\ntimeline = []\nscaling = 10\nstart_date = date(2008, 5, 1)\nend_date = date(2008, 6, 1)\n\nnum_days = (end_date - start_date).days + 1\ndays = [start_date + timedelta(i) for i in range(num_days)]\nrequests = [int(scaling * weibullvariate(0.5, 2)) for i in range(num_days)]\ntimeline = zip(days, requests)\ntimeline\n</code></pre>\n"}, {'answer_id': 56102, 'author': 'Jacob Rigby', 'author_id': 5357, 'author_profile': 'https://Stackoverflow.com/users/5357', 'pm_score': 0, 'selected': False, 'text': '<p>I rewrote the code above to be shorter (but maybe it\'s too obfuscated now?)</p>\n\n<pre><code>timeline = (start_date + timedelta(days=days) for days in count(0))\nhow_many_days = (end_date - start_date).days\npick_a_day = lambda _:int(how_many_days * weibullvariate(0.5, 2))\ndays = sorted(imap(pick_a_day, xrange(how_many_responses)))\nhistogram = zip(timeline, (len(list(responses)) for day, responses in groupby(days)))\nprint \'\\n\'.join((d.strftime(\'%Y-%m-%d \') + "*" * c) for d,c in histogram)\n</code></pre>\n'}, {'answer_id': 56247, 'author': 'Kai', 'author_id': 2963, 'author_profile': 'https://Stackoverflow.com/users/2963', 'pm_score': 1, 'selected': False, 'text': '<p>Slightly longer but probably more readable rework of your last four lines:</p>\n\n<pre><code>samples = [0 for i in xrange(how_many_days + 1)]\nfor s in xrange(how_many_responses):\n    samples[min(int(how_many_days * weibullvariate(0.5, 2)), how_many_days)] += 1\nhistogram = zip(timeline, samples)\nprint \'\\n\'.join((d.strftime(\'%Y-%m-%d \') + "*" * c) for d,c in histogram)\n</code></pre>\n\n<p>This always drops the samples within the date range, but you get a corresponding bump at the end of the timeline from all of the samples that are above the [0, 1] range.</p>\n'}, {'answer_id': 56548, 'author': 'Kai', 'author_id': 2963, 'author_profile': 'https://Stackoverflow.com/users/2963', 'pm_score': 2, 'selected': True, 'text': '<p>This is quick and probably not that accurate, but if you calculate the PDF yourself, then at least you make it easier to lay several smaller/larger ones on a single timeline. <code>dev</code> is the std deviation in the Guassian noise, which controls the roughness. Note that this is <em>not</em> the \'right\' way to generate what you want, but it\'s easy.</p>\n\n<pre><code>import math\nfrom datetime import datetime, timedelta, date\nfrom random import gauss\n\nhow_many_responses = 1000\nstart_date = date(2008, 5, 1)\nend_date = date(2008, 6, 1)\nnum_days = (end_date - start_date).days + 1\ntimeline = [start_date + timedelta(i) for i in xrange(num_days)]\n\ndef weibull(x, k, l):\n    return (k / l) * (x / l)**(k-1) * math.e**(-(x/l)**k)\n\ndev = 0.1\nsamples = [i * 1.25/(num_days-1) for i in range(num_days)]\nprobs = [weibull(i, 2, 0.5) for i in samples]\nnoise = [gauss(0, dev) for i in samples]\nsimdata = [max(0., e + n) for (e, n) in zip(probs, noise)]\nevents = [int(p * (how_many_responses / sum(probs))) for p in simdata]\n\nhistogram = zip(timeline, events)\n\nprint \'\\n\'.join((d.strftime(\'%Y-%m-%d \') + "*" * c) for d,c in histogram)\n</code></pre>\n'}, {'answer_id': 89140, 'author': 'Gregg Lind', 'author_id': 15842, 'author_profile': 'https://Stackoverflow.com/users/15842', 'pm_score': 0, 'selected': False, 'text': '<p>Another solution is to use <a href="http://rpy.sourceforge.net/" rel="nofollow noreferrer">Rpy</a>, which puts all of the power of R (including lots of tools for distributions), easily into Python.  </p>\n'}]
['https://Stackoverflow.com/questions/53786', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/5357/']