From a7abb0e372091d88eaf6cd2cd0d4e0b09d570423 Mon Sep 17 00:00:00 2001 From: Chancy Kennedy Date: Sun, 8 Jun 2014 20:15:23 -0500 Subject: [PATCH 1/4] DOC: existence docs and benchmarks. --- bench/bench_existence.py | 140 +++++++++++++++++++++++++++ doc/source/enhancingperf.rst | 177 +++++++++++++++++++++++++++++++++++ 2 files changed, 317 insertions(+) create mode 100644 bench/bench_existence.py diff --git a/bench/bench_existence.py b/bench/bench_existence.py new file mode 100644 index 0000000000000..05ca91a78ae8d --- /dev/null +++ b/bench/bench_existence.py @@ -0,0 +1,140 @@ +from timeit import Timer +import pandas as pd +import matplotlib.pyplot as plt +import os + + +class Benchmarks(object): + + def removed_time_py_list(look_for, look_in): + l = range(look_in) + df = pd.DataFrame(range(look_for)) + + def time_this(): + df[[x in l for x in df.index.values]] + + return time_this + + def time_py_dict(look_for, look_in): + l = range(look_in) + l_dict = dict(zip(l, l)) + df = pd.DataFrame(range(look_for)) + + def time_this(): + df[[x in l_dict for x in df.index.values]] + + return time_this + + + def time_isin_list(look_for, look_in): + l = range(look_in) + df = pd.DataFrame(range(look_for)) + + def time_this(): + df[df.index.isin(l)] + + return time_this + + + def time_isin_dict(look_for, look_in): + l = range(look_in) + l_dict = dict(zip(l, l)) + df = pd.DataFrame(range(look_for)) + + def time_this(): + df[df.index.isin(l_dict)] + + return time_this + + + def time_isin_series(look_for, look_in): + l = range(look_in) + l_series = pd.Series(l) + df = pd.DataFrame(range(look_for)) + + def time_this(): + df[df.index.isin(l_series.index)] + + return time_this + + + def time_join(look_for, look_in): + l = range(look_in) + l_series = pd.Series(l) + l_series.name = 'data' + df = pd.DataFrame(range(look_for)) + + def time_this(): + df.join(l_series, how='inner') + + return time_this + + def time_query_eqeq(look_for, look_in): + l = range(look_in) + s = pd.Series(l) + s.name = 'data' + df = pd.DataFrame(range(look_for)) + + def time_this(): + l_series = s + df.query('index == @l_series') + + return time_this + + def time_query_in(look_for, look_in): + l = range(look_in) + s = pd.Series(l) + s.name = 'data' + df = pd.DataFrame(range(look_for)) + + def time_this(): + l_series = s + df.query('index in @l_series') + + return time_this + + +def run_bench(to_time, repeat, look_in, num_look_for_rows, y_limit, filename): + func_results = [] + plt.figure() + + for time_func_name in to_time: + plot_results = [] + for look_for in num_look_for_rows: + func = Benchmarks.__dict__[time_func_name](look_for, look_in) + t = Timer(func) + elapsed = t.timeit(number=repeat) / repeat + name = time_func_name.replace('time_', '') + func_results.append((name, look_for, look_in, elapsed)) + plot_results.append(elapsed) + plt.plot(num_look_for_rows, plot_results, label=name) + + plt.axes().set_xscale('log') + x1,x2,y1,y2 = plt.axis() + plt.axis((x1, x2, 0, y_limit)) + + plt.legend(loc=2, prop={'size':8}) + plt.title('Look in %s Rows' % look_in) + plt.xlabel('Look For X Rows') + plt.ylabel('Time(s)') + plt.savefig(filename) + plt.clf() + + +if __name__ == '__main__': + + pandas_dir = os.path.dirname(os.path.abspath(os.path.dirname(__file__))) + static_path = os.path.join(pandas_dir, 'doc', 'source', '_static') + + join = lambda p: os.path.join(static_path, p) + + to_time = [key for key in Benchmarks.__dict__ if key.startswith('time_')] + + num_look_for_rows = [10 * 2**i for i in range(1, 21)] + + filename = join('existence-perf-small.png') + run_bench(to_time, 10, 5000, num_look_for_rows[0:len(num_look_for_rows)/2], 0.004, filename) + + filename = join('existence-perf-large.png') + run_bench(to_time, 3, 5000000, num_look_for_rows[len(num_look_for_rows)/2:], 10, filename) + diff --git a/doc/source/enhancingperf.rst b/doc/source/enhancingperf.rst index 00c76632ce17b..86a08072ae168 100644 --- a/doc/source/enhancingperf.rst +++ b/doc/source/enhancingperf.rst @@ -668,3 +668,180 @@ In general, :meth:`DataFrame.query`/:func:`pandas.eval` will evaluate the subexpressions that *can* be evaluated by ``numexpr`` and those that must be evaluated in Python space transparently to the user. This is done by inferring the result type of an expression from its arguments and operators. + +Existence (IsIn, Inner Join, Dict/Hash, Query) +---------------------------------------------------- + +There are a number of different ways to test for existence using pandas. The +following methods can be used to achieve an existence test. The comments correspond +to the legend in the plots further down. + + +:meth:`DataFrame.isin` + +.. code-block:: python + + # isin_list + df[df.index.isin(lst)] + # isin_dict + df[df.index.isin(dct)] + # isin_series + df[df.index.isin(series)] + + + +:meth:`DataFrame.query` + +.. code-block:: python + + # query_in list + df.query('index in @lst') + # query_in Series + df.query('index in @series') + # query_in dict + df.query('index in @dct') + + # query_eqeq list + df.query('index == @lst') + # query_eqeq Series + df.query('index == @series') + + # dict actually throws an error with '==' + + + +:meth:`DataFrame.apply` + +.. code-block:: python + + df[df.index.apply(lambda x: x in lst)] + + + +:meth:`DataFrame.join` + +.. code-block:: python + + # join + df.join(lst, how='inner') + + # this can actually be fast for small DataFrames + df[[x in dct for x in df.index]] + + # isin_series, query_eqeq Series, query_in Series, pydict, + # join and isin_list are included in the plots below. + + +As seen below, generally using a ``Series`` is better than using pure python data +structures for anything larger than very small datasets of around 1000 records. +The fastest two being ``query('col == @series')`` and ``join(series)``: + +.. code-block:: python + + lst = range(1000000) + series = Series(lst, name='data') + + df = DataFrame(lst, columns=['ID']) + + df.query('index == @series') + # 10 loops, best of 3: 82.9 ms per loop + + df.join(series, how='inner') + # 100 loops, best of 3: 19.2 ms per loop + +list vs Series: + +.. code-block:: python + + df[df.index.isin(lst)] + # 1 loops, best of 3: 1.06 s per loop + + df[df.index.isin(series)] + # 1 loops, best of 3: 477 ms per loop + +df.index vs df.column doesn't make a difference here: + +.. code-block:: python + + df[df.ID.isin(series)] + # 1 loops, best of 3: 474 ms per loop + + df[df.index.isin(series)] + # 1 loops, best of 3: 475 ms per loop + +The ``query`` 'in' syntax has the same performance as ``isin``, except +for when using '==' with a ``Series``: + +.. code-block:: python + + df.query('index in @lst') + # 1 loops, best of 3: 1.04 s per loop + + df.query('index in @series') + # 1 loops, best of 3: 451 ms per loop + + df.query('index == @lst') + # 1 loops, best of 3: 1.03 s per loop + +'==' is actually quite a bit faster than 'in' when used against a Series +but not as fast as ``join``. + +.. code-block:: python + + df.query('index == @series') + # 10 loops, best of 3: 80.5 ms per loop + +For ``join``, the data must be the index in the ``DataFrame`` and the index in the ``Series`` +for the best performance. The ``Series`` must also have a ``name``. ``join`` defaults to a +left join so we need to specify 'inner' for existence. + +.. code-block:: python + + df.join(series, how='inner') + # 100 loops, best of 3: 19.7 ms per loop + +Smaller datasets: + +.. code-block:: python + + df = DataFrame([1,2,3,4], columns=['ID']) + lst = range(10000) + dct = dict(zip(lst, lst)) + series = Series(lst, name='data') + + df.join(series, how='inner') + # 1000 loops, best of 3: 866 us per loop + + df[df.ID.isin(dct)] + # 1000 loops, best of 3: 809 us per loop + + df[df.ID.isin(lst)] + # 1000 loops, best of 3: 853 us per loop + + df[df.ID.isin(series)] + # 100 loops, best of 3: 2.22 ms per loop + +It's actually faster to use ``apply`` or a list comprehension for these small cases. + +.. code-block:: python + + df[[x in dct for x in df.ID]] + # 1000 loops, best of 3: 266 us per loop + + df[df.ID.apply(lambda x: x in dct)] + # 1000 loops, best of 3: 364 us per loop + + +Here is a visualization of some of the benchmarks above. You can see that except for with +very small datasets, ``isin(Series)``, ``join(Series)``, and ``query('col == Series')`` +quickly become faster than the pure python data structures. + +.. image:: _static/existence-perf-small.png + +However, ``isin(Series)`` still presents fairly poor exponential performance where ``join`` is quite +fast for large datasets. There is some overhead involved in ensuring your data is the index +in both your left and right datasets but that time should be clearly outweighed by the gains of +the join itself. For extremely large datasets, you may start bumping into memory limits since ``join`` +does not perform any disk chunking, etc. + +.. image:: _static/existence-perf-large.png \ No newline at end of file From cf284ce60e6636428811d096f197461dfb304459 Mon Sep 17 00:00:00 2001 From: Chancy Kennedy Date: Wed, 11 Jun 2014 01:23:08 -0500 Subject: [PATCH 2/4] DOC: existence docs and benchmarks. --- bench/bench_existence.py | 23 +++++++++--------- doc/source/enhancingperf.rst | 47 +++++++++++++++--------------------- 2 files changed, 31 insertions(+), 39 deletions(-) diff --git a/bench/bench_existence.py b/bench/bench_existence.py index 05ca91a78ae8d..fa09d10485e2b 100644 --- a/bench/bench_existence.py +++ b/bench/bench_existence.py @@ -68,18 +68,19 @@ def time_this(): df.join(l_series, how='inner') return time_this - - def time_query_eqeq(look_for, look_in): - l = range(look_in) - s = pd.Series(l) - s.name = 'data' - df = pd.DataFrame(range(look_for)) - - def time_this(): - l_series = s - df.query('index == @l_series') - return time_this + # Removed. This functionality might be a bug in query('.. == ..'). + # def time_query_eqeq(look_for, look_in): + # l = range(look_in) + # s = pd.Series(l) + # s.name = 'data' + # df = pd.DataFrame(range(look_for)) + + # def time_this(): + # l_series = s + # df.query('index == @l_series') + + # return time_this def time_query_in(look_for, look_in): l = range(look_in) diff --git a/doc/source/enhancingperf.rst b/doc/source/enhancingperf.rst index 86a08072ae168..554543c0f51df 100644 --- a/doc/source/enhancingperf.rst +++ b/doc/source/enhancingperf.rst @@ -672,9 +672,13 @@ by inferring the result type of an expression from its arguments and operators. Existence (IsIn, Inner Join, Dict/Hash, Query) ---------------------------------------------------- -There are a number of different ways to test for existence using pandas. The -following methods can be used to achieve an existence test. The comments correspond -to the legend in the plots further down. +Existence is the process of testing if an item exists in another list of items, and +in the case of a DataFrame, we're testing each value of a column for existence in +another collection of items. + +There are a number of different ways to test for existence using pandas and the +following methods are a few of those. The comments correspond to the legend +in the plots further down. :meth:`DataFrame.isin` @@ -694,20 +698,19 @@ to the legend in the plots further down. .. code-block:: python + # The '@' symbol is used with `query` to reference local variables. Names + # without '@' will reference the DataFrame's columns or index. + # query_in list df.query('index in @lst') # query_in Series df.query('index in @series') - # query_in dict - df.query('index in @dct') - # query_eqeq list - df.query('index == @lst') - # query_eqeq Series - df.query('index == @series') - - # dict actually throws an error with '==' + # A list can be used with `query('.. == ..')` to test for existence + # but other data structures such as the `pandas.Series` have + # a different behaviour. + df.query('index == @lst') :meth:`DataFrame.apply` @@ -717,7 +720,6 @@ to the legend in the plots further down. df[df.index.apply(lambda x: x in lst)] - :meth:`DataFrame.join` .. code-block:: python @@ -728,13 +730,13 @@ to the legend in the plots further down. # this can actually be fast for small DataFrames df[[x in dct for x in df.index]] - # isin_series, query_eqeq Series, query_in Series, pydict, + # isin_series, query_in Series, pydict, # join and isin_list are included in the plots below. As seen below, generally using a ``Series`` is better than using pure python data structures for anything larger than very small datasets of around 1000 records. -The fastest two being ``query('col == @series')`` and ``join(series)``: +The fastest two being ``join(series)``: .. code-block:: python @@ -743,9 +745,6 @@ The fastest two being ``query('col == @series')`` and ``join(series)``: df = DataFrame(lst, columns=['ID']) - df.query('index == @series') - # 10 loops, best of 3: 82.9 ms per loop - df.join(series, how='inner') # 100 loops, best of 3: 19.2 ms per loop @@ -769,8 +768,7 @@ df.index vs df.column doesn't make a difference here: df[df.index.isin(series)] # 1 loops, best of 3: 475 ms per loop -The ``query`` 'in' syntax has the same performance as ``isin``, except -for when using '==' with a ``Series``: +The ``query`` 'in' syntax has the same performance as ``isin``. .. code-block:: python @@ -783,13 +781,6 @@ for when using '==' with a ``Series``: df.query('index == @lst') # 1 loops, best of 3: 1.03 s per loop -'==' is actually quite a bit faster than 'in' when used against a Series -but not as fast as ``join``. - -.. code-block:: python - - df.query('index == @series') - # 10 loops, best of 3: 80.5 ms per loop For ``join``, the data must be the index in the ``DataFrame`` and the index in the ``Series`` for the best performance. The ``Series`` must also have a ``name``. ``join`` defaults to a @@ -833,8 +824,8 @@ It's actually faster to use ``apply`` or a list comprehension for these small ca Here is a visualization of some of the benchmarks above. You can see that except for with -very small datasets, ``isin(Series)``, ``join(Series)``, and ``query('col == Series')`` -quickly become faster than the pure python data structures. +very small datasets, ``isin(Series)`` and ``join(Series)`` quickly become faster than the +pure python data structures. .. image:: _static/existence-perf-small.png From 02e207ecde5e75a00af7bf90f8545c3a2b70957e Mon Sep 17 00:00:00 2001 From: Chancy Kennedy Date: Mon, 23 Jun 2014 21:08:31 -0500 Subject: [PATCH 3/4] DOC: existence docs and benchmarks. --- doc/source/_static/existence-perf-large.png | Bin 0 -> 43310 bytes doc/source/_static/existence-perf-small.png | Bin 0 -> 58999 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/source/_static/existence-perf-large.png create mode 100644 doc/source/_static/existence-perf-small.png diff --git a/doc/source/_static/existence-perf-large.png b/doc/source/_static/existence-perf-large.png new file mode 100644 index 0000000000000000000000000000000000000000..5c0766a2afb3ca74edb567d4c6afaca486307bd9 GIT binary patch literal 43310 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 W4z20ukBa96zH~J7)F04nL;nZ+Wr7a? literal 0 HcmV?d00001 diff --git a/doc/source/_static/existence-perf-small.png b/doc/source/_static/existence-perf-small.png new file mode 100644 index 0000000000000000000000000000000000000000..6150cc47037a52fc39ac4400ee0675d665174bb7 GIT binary patch literal 58999 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 Date: Sun, 14 Sep 2014 20:07:46 -0500 Subject: [PATCH 4/4] Benchmark using random data. --- bench/bench_existence.py | 304 ++++++++++++++++++++++++++++----------- 1 file changed, 224 insertions(+), 80 deletions(-) diff --git a/bench/bench_existence.py b/bench/bench_existence.py index fa09d10485e2b..a8487cdcd76ec 100644 --- a/bench/bench_existence.py +++ b/bench/bench_existence.py @@ -1,141 +1,285 @@ +from __future__ import division + +import os +import sys +from itertools import cycle + from timeit import Timer import pandas as pd +import numpy as np import matplotlib.pyplot as plt -import os +from bokeh.mpl import to_bokeh +from numpy.random import randint +from mpltools import style +style.use('ggplot') -class Benchmarks(object): +class ExistenceBenchmarks(object): - def removed_time_py_list(look_for, look_in): - l = range(look_in) - df = pd.DataFrame(range(look_for)) - - def time_this(): - df[[x in l for x in df.index.values]] - - return time_this def time_py_dict(look_for, look_in): - l = range(look_in) - l_dict = dict(zip(l, l)) - df = pd.DataFrame(range(look_for)) + df_look_for = pd.DataFrame(look_for, columns=['data']) + dict_look_in = dict(zip(look_in, look_in)) def time_this(): - df[[x in l_dict for x in df.index.values]] + result = df_look_for[[x in dict_look_in for x in df_look_for.data]] + return result.drop_duplicates().sort('data') return time_this def time_isin_list(look_for, look_in): - l = range(look_in) - df = pd.DataFrame(range(look_for)) + df_look_for = pd.DataFrame(look_for, columns=['data']) + list_look_in = list(look_in) def time_this(): - df[df.index.isin(l)] + result = df_look_for[df_look_for.data.isin(list_look_in)] + return result.drop_duplicates().sort('data') return time_this def time_isin_dict(look_for, look_in): - l = range(look_in) - l_dict = dict(zip(l, l)) - df = pd.DataFrame(range(look_for)) + df_look_for = pd.DataFrame(look_for, columns=['data']) + dict_look_in = dict(zip(look_in, look_in)) def time_this(): - df[df.index.isin(l_dict)] + result = df_look_for[df_look_for.data.isin(dict_look_in)] + return result.drop_duplicates().sort('data') return time_this def time_isin_series(look_for, look_in): - l = range(look_in) - l_series = pd.Series(l) - df = pd.DataFrame(range(look_for)) + series_look_in = pd.Series(look_in) + df_look_for = pd.DataFrame(look_for, columns=['data']) def time_this(): - df[df.index.isin(l_series.index)] + result = df_look_for[df_look_for.data.isin(series_look_in)] + return result.drop_duplicates().sort('data') return time_this def time_join(look_for, look_in): - l = range(look_in) - l_series = pd.Series(l) - l_series.name = 'data' - df = pd.DataFrame(range(look_for)) + series_look_in = pd.Series(look_in, index=look_in) + series_look_in.name = 'series_data' + df_look_for = pd.DataFrame(look_for, columns=['data'], index=look_for) def time_this(): - df.join(l_series, how='inner') + result = df_look_for.join(series_look_in, how='inner') + return result.drop_duplicates() return time_this - - # Removed. This functionality might be a bug in query('.. == ..'). - # def time_query_eqeq(look_for, look_in): - # l = range(look_in) - # s = pd.Series(l) - # s.name = 'data' - # df = pd.DataFrame(range(look_for)) - - # def time_this(): - # l_series = s - # df.query('index == @l_series') - - # return time_this + + + def time_join_no_dups(look_for, look_in): + series_look_in = pd.Series(look_in, index=look_in) + series_look_in.name = 'series_data' + df_look_for = pd.DataFrame(look_for, columns=['data'], index=look_for) + + def time_this(): + df_look_for.drop_duplicates(inplace=True) + series_look_in.drop_duplicates(inplace=True) + result = df_look_for.join(series_look_in, how='inner') + return result.sort('data') + + return time_this + def time_query_in(look_for, look_in): - l = range(look_in) - s = pd.Series(l) - s.name = 'data' - df = pd.DataFrame(range(look_for)) + series_look_in = pd.Series(look_in) + series_look_in.name = 'data' + df_look_for = pd.DataFrame(look_for, columns=['data']) def time_this(): - l_series = s - df.query('index in @l_series') + # series_look_in is not visible to .query unless defined in local function scope. + s_look_in = series_look_in + result = df_look_for.query('data in @s_look_in') + return result.drop_duplicates().sort('data') return time_this -def run_bench(to_time, repeat, look_in, num_look_for_rows, y_limit, filename): +def run_bench(to_time, repeat, look_sets, x_axis, linestyle='-'): func_results = [] - plt.figure() + markers = cycle(['o', 's', '+', '^', 'v', 'x', 'D', '*']) for time_func_name in to_time: - plot_results = [] - for look_for in num_look_for_rows: - func = Benchmarks.__dict__[time_func_name](look_for, look_in) - t = Timer(func) - elapsed = t.timeit(number=repeat) / repeat - name = time_func_name.replace('time_', '') - func_results.append((name, look_for, look_in, elapsed)) - plot_results.append(elapsed) - plt.plot(num_look_for_rows, plot_results, label=name) - - plt.axes().set_xscale('log') - x1,x2,y1,y2 = plt.axis() - plt.axis((x1, x2, 0, y_limit)) - - plt.legend(loc=2, prop={'size':8}) - plt.title('Look in %s Rows' % look_in) - plt.xlabel('Look For X Rows') - plt.ylabel('Time(s)') - plt.savefig(filename) - plt.clf() + marker=markers.next() + colors = cycle(['b', 'g', 'r', 'c', 'm', 'y', 'k']) + for set_name, look_set in look_sets: + color=colors.next() + plot_results = [] + for look_for, look_in in look_set: + func = ExistenceBenchmarks.__dict__[time_func_name](look_for, look_in) + result = func() + t = Timer(func) + elapsed = t.timeit(number=repeat) / repeat + name = time_func_name.replace('time_', '') + ' ' + set_name + ' (%.1f%%)' % ((len(result) / len(look_for)) * 100) + func_results.append((name, look_for, look_in, elapsed)) + plot_results.append(elapsed) + plt.plot(x_axis, plot_results, marker=marker, color=color, label=name, linestyle=linestyle) + +def test_timed(to_time): + look_for = randint(0, 10000, 5000) + look_in = randint(5000, 15000, 5000) + + first_result = ExistenceBenchmarks.__dict__[to_time[0]](look_for, look_in)() + for time_func_name in to_time[1:]: + func = ExistenceBenchmarks.__dict__[time_func_name](look_for, look_in) + result = func() + if np.array_equal(first_result['data'].values, result['data'].values): + pass + else: + raise AssertionError("%s and %s have unmatched output." % (to_time[0], time_func_name)) + + if __name__ == '__main__': pandas_dir = os.path.dirname(os.path.abspath(os.path.dirname(__file__))) static_path = os.path.join(pandas_dir, 'doc', 'source', '_static') + join_path = lambda p: os.path.join(static_path, p) - join = lambda p: os.path.join(static_path, p) - - to_time = [key for key in Benchmarks.__dict__ if key.startswith('time_')] - - num_look_for_rows = [10 * 2**i for i in range(1, 21)] + to_time = [key for key in ExistenceBenchmarks.__dict__ if key.startswith('time_')] - filename = join('existence-perf-small.png') - run_bench(to_time, 10, 5000, num_look_for_rows[0:len(num_look_for_rows)/2], 0.004, filename) - filename = join('existence-perf-large.png') - run_bench(to_time, 3, 5000000, num_look_for_rows[len(num_look_for_rows)/2:], 10, filename) + if len(sys.argv) != 2: + print 'usage: <--test, --run>' + print '\t--test : Ensure that all timed functions are returning identical output.' + print '\t--run : Generate plots for all timed functions.' + sys.exit() + if sys.argv[1] == '--test': + test_timed(to_time) + + elif sys.argv[1] == '--run': + test_timed(to_time) + + def save_plot(filename, subtitle): + fname = join_path(filename) + plt.axes().set_xscale('log') + x1,x2,y1,y2 = plt.axis() + # plt.axis((x1, x2, 0, y_limit)) + plt.legend(loc=2, prop={'size':8}) + plt.title('Existence Comparisons%s' % subtitle) + plt.xlabel('% Overlap of X Elements') + plt.ylabel('Time(s)') + plt.savefig(fname) + plt.clf() + + def unordered(exp_range, repeat): + rng = [2**x for x in exp_range] + + # 25% overlap + look_set_25 = \ + [(randint(0, 100*i, 50*i), randint(75*i, 175*i, 50*i)) for i in rng] + + look_set_50 = \ + [(randint(0, 100*i, 50*i), randint(50*i, 150*i, 50*i)) for i in rng] + + look_set_75 = \ + [(randint(0, 100*i, 50*i), randint(25*i, 125*i, 50*i)) for i in rng] + + look_set_100 = \ + [(randint(0, 100*i, 50*i), randint(0*i, 100*i, 50*i)) for i in rng] + + look_sets = [] + look_sets.append(('25% overlap', look_set_25)) + look_sets.append(('50% overlap', look_set_50)) + look_sets.append(('75% overlap', look_set_75)) + look_sets.append(('100% overlap', look_set_100)) + + x_axis = [100*i for i in rng] + run_bench(to_time, 10, look_sets, x_axis, linestyle='-') + + + def from_ordered(exp_range, repeat): + rng = [2**x for x in exp_range] + + # 25% overlap + look_set_25 = \ + [(sorted(randint(0, 100*i, 50*i)), randint(75*i, 175*i, 50*i)) for i in rng] + + look_set_50 = \ + [(sorted(randint(0, 100*i, 50*i)), randint(50*i, 150*i, 50*i)) for i in rng] + + look_set_75 = \ + [(sorted(randint(0, 100*i, 50*i)), randint(25*i, 125*i, 50*i)) for i in rng] + + look_set_100 = \ + [(sorted(randint(0, 100*i, 50*i)), randint(0*i, 100*i, 50*i)) for i in rng] + + look_sets = [] + look_sets.append(('25% overlap, for-ordered', look_set_25)) + look_sets.append(('50% overlap, for-ordered', look_set_50)) + look_sets.append(('75% overlap, for-ordered', look_set_75)) + look_sets.append(('100% overlap, for-ordered', look_set_100)) + + x_axis = [100*i for i in rng] + run_bench(to_time, 10, look_sets, x_axis, linestyle='-.') + + + def both_ordered(exp_range, repeat): + rng = [2**x for x in exp_range] + + # 25% overlap + look_set_25 = \ + [(sorted(randint(0, 100*i, 50*i)), sorted(randint(75*i, 175*i, 50*i))) for i in rng] + + look_set_50 = \ + [(sorted(randint(0, 100*i, 50*i)), sorted(randint(50*i, 150*i, 50*i))) for i in rng] + + look_set_75 = \ + [(sorted(randint(0, 100*i, 50*i)), sorted(randint(25*i, 125*i, 50*i))) for i in rng] + + look_set_100 = \ + [(sorted(randint(0, 100*i, 50*i)), sorted(randint(0*i, 100*i, 50*i))) for i in rng] + + look_sets = [] + look_sets.append(('25% overlap, both-ordered', look_set_25)) + look_sets.append(('50% overlap, both-ordered', look_set_50)) + look_sets.append(('75% overlap, both-ordered', look_set_75)) + look_sets.append(('100% overlap, both-ordered', look_set_100)) + + x_axis = [100*i for i in rng] + run_bench(to_time, repeat, look_sets, x_axis, linestyle=':') + + + plt.figure(figsize=(32, 24)) + unordered(range(1, 10), 10) + from_ordered(range(1, 10), 10) + both_ordered(range(1, 10), 10) + save_plot('existence-perf-small.png', ': Small') + + plt.figure(figsize=(32, 24)) + unordered(range(10, 15), 3) + from_ordered(range(10, 15), 3) + both_ordered(range(10, 15), 3) + save_plot('existence-perf-large.png', ': Large') + + plt.figure(figsize=(16, 12)) + unordered(range(1, 10), 10) + save_plot('existence-perf-unordered-small.png', ': Unordered Small') + + plt.figure(figsize=(16, 12)) + from_ordered(range(1, 10), 10) + save_plot('existence-perf-from-ordered-small.png', ': From-Ordered Small') + + plt.figure(figsize=(16, 12)) + both_ordered(range(1, 10), 10) + save_plot('existence-perf-both-ordered-small.png', ': Both-Ordered Small') + + plt.figure(figsize=(16, 12)) + unordered(range(10, 15), 3) + save_plot('existence-perf-unordered-large.png', ': Unordered Large') + + plt.figure(figsize=(16, 12)) + from_ordered(range(10, 15), 3) + save_plot('existence-perf-from-ordered-large.png', ': From-Ordered Large') + + plt.figure(figsize=(16, 12)) + both_ordered(range(10, 15), 3) + save_plot('existence-perf-both-ordered-large.png', ': Both-Ordered Large') \ No newline at end of file