From d047ded65814a0900a2f415d702dfc61b2d358fe Mon Sep 17 00:00:00 2001 From: Bill Zorn Date: Sun, 6 Dec 2015 14:26:02 -0800 Subject: [PATCH] ipython and beginning of pairing --- mtg_sweep1.ipynb | 333 +++++++++++++++++++++++ scripts/analysis.py | 24 +- scripts/{validate.py => mtg_validate.py} | 0 scripts/pairing.py | 36 +++ 4 files changed, 384 insertions(+), 9 deletions(-) create mode 100644 mtg_sweep1.ipynb rename scripts/{validate.py => mtg_validate.py} (100%) create mode 100755 scripts/pairing.py diff --git a/mtg_sweep1.ipynb b/mtg_sweep1.ipynb new file mode 100644 index 0000000..96a9b9c --- /dev/null +++ b/mtg_sweep1.ipynb @@ -0,0 +1,333 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening encoded card file: /home/mtgencode/data/output.txt\n", + "15065 valid, 0 skipped, 0 invalid, 0 failed to parse.\n", + "generating 3-gram model\n", + "found 25046 sentences\n", + "\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/baseline_128_1_epoch50.00_0.3941.output.1.0.txt\n", + "5549 valid, 0 skipped, 88 invalid, 29 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/baseline_128_2_epoch50.00_0.3862.output.1.0.txt\n", + "5360 valid, 0 skipped, 121 invalid, 35 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/baseline_256_1_epoch50.00_0.2736.output.1.0.txt\n", + "5732 valid, 0 skipped, 71 invalid, 17 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/baseline_256_2_epoch50.00_0.2645.output.1.0.txt\n", + "5348 valid, 0 skipped, 50 invalid, 29 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/baseline_384_1_epoch50.00_0.2117.output.1.0.txt\n", + "5889 valid, 0 skipped, 56 invalid, 15 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/baseline_384_2_epoch50.00_0.2102.output.1.0.txt\n", + "5887 valid, 0 skipped, 57 invalid, 35 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/baseline_512_1_epoch50.00_0.1952.output.1.0.txt\n", + "5701 valid, 0 skipped, 44 invalid, 32 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/baseline_512_2_epoch50.00_0.1807.output.1.0.txt\n", + "5711 valid, 0 skipped, 49 invalid, 24 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/baseline_640_1_epoch50.00_0.1798.output.1.0.txt\n", + "5726 valid, 0 skipped, 41 invalid, 33 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/baseline_768_1_epoch46.51_0.1834.output.1.0.txt\n", + "5949 valid, 0 skipped, 69 invalid, 18 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/baseline_768_2_epoch46.51_0.1906.output.1.0.txt\n", + "5808 valid, 0 skipped, 64 invalid, 23 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-25_128_1_epoch50.00_0.4641.output.1.0.txt\n", + "5660 valid, 0 skipped, 24 invalid, 19 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/baseline_640_2_epoch50.00_0.1870.output.1.0.txt\n", + "5859 valid, 0 skipped, 51 invalid, 16 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-50_128_1_epoch50.00_0.5686.output.1.0.txt\n", + "5983 valid, 0 skipped, 33 invalid, 17 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-50_256_2_epoch50.00_0.4287.output.1.0.txt\n", + "5895 valid, 0 skipped, 19 invalid, 2 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-25_384_1_epoch50.00_0.3018.output.1.0.txt\n", + "6081 valid, 0 skipped, 10 invalid, 6 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-25_384_2_epoch50.00_0.2956.output.1.0.txt\n", + "6081 valid, 0 skipped, 22 invalid, 9 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-25_512_1_epoch50.00_0.2663.output.1.0.txt\n", + "5999 valid, 0 skipped, 25 invalid, 8 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-25_512_2_epoch50.00_0.2597.output.1.0.txt\n", + "6263 valid, 0 skipped, 26 invalid, 8 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-25_640_1_epoch50.00_0.2385.output.1.0.txt\n", + "5863 valid, 0 skipped, 20 invalid, 5 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-25_640_2_epoch50.00_0.2361.output.1.0.txt\n", + "5941 valid, 0 skipped, 7 invalid, 11 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-25_768_1_epoch49.83_0.2268.output.1.0.txt\n", + "6065 valid, 0 skipped, 9 invalid, 5 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-25_768_2_epoch50.00_0.2412.output.1.0.txt\n", + "5919 valid, 0 skipped, 15 invalid, 10 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-50_384_1_epoch50.00_0.3633.output.1.0.txt\n", + "5997 valid, 0 skipped, 9 invalid, 2 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-50_384_2_epoch50.00_0.3636.output.1.0.txt\n", + "6047 valid, 0 skipped, 17 invalid, 4 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-50_512_1_epoch50.00_0.3269.output.1.0.txt\n", + "5894 valid, 0 skipped, 10 invalid, 9 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-50_512_2_epoch50.00_0.3231.output.1.0.txt\n", + "5942 valid, 0 skipped, 7 invalid, 2 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-50_640_1_epoch50.00_0.3062.output.1.0.txt\n", + "6002 valid, 0 skipped, 14 invalid, 1 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-50_640_2_epoch50.00_0.3100.output.1.0.txt\n", + "6057 valid, 0 skipped, 12 invalid, 3 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-50_768_1_epoch49.83_0.2923.output.1.0.txt\n", + "5896 valid, 0 skipped, 11 invalid, 5 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-50_768_2_epoch49.83_0.2919.output.1.0.txt\n", + "6047 valid, 0 skipped, 7 invalid, 3 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-25_128_2_epoch50.00_0.4641.output.1.0.txt\n", + "6226 valid, 0 skipped, 36 invalid, 13 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-25_256_1_epoch50.00_0.3579.output.1.0.txt\n", + "5910 valid, 0 skipped, 23 invalid, 6 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-25_256_2_epoch50.00_0.3554.output.1.0.txt\n", + "5801 valid, 0 skipped, 22 invalid, 8 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-50_128_2_epoch50.00_0.5690.output.1.0.txt\n", + "6339 valid, 0 skipped, 51 invalid, 22 failed to parse.\n", + "Opening encoded card file: /data/collected/mtg-rnn-sweep1/dropout-50_256_1_epoch50.00_0.4198.output.1.0.txt\n", + "5995 valid, 0 skipped, 12 invalid, 10 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/baseline_128_1_epoch37.50_0.4733.output.1.0.txt\n", + "5871 valid, 0 skipped, 130 invalid, 42 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/baseline_128_2_epoch45.83_0.4774.output.1.0.txt\n", + "5429 valid, 0 skipped, 129 invalid, 45 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/baseline_256_1_epoch16.67_0.4530.output.1.0.txt\n", + "5808 valid, 0 skipped, 124 invalid, 38 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/baseline_256_2_epoch16.67_0.4552.output.1.0.txt\n", + "5796 valid, 0 skipped, 103 invalid, 44 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/baseline_384_1_epoch12.50_0.4431.output.1.0.txt\n", + "5890 valid, 0 skipped, 111 invalid, 48 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/baseline_384_2_epoch12.50_0.4469.output.1.0.txt\n", + "5699 valid, 0 skipped, 98 invalid, 41 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/baseline_512_1_epoch12.50_0.4374.output.1.0.txt\n", + "5698 valid, 0 skipped, 138 invalid, 49 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/baseline_512_2_epoch12.50_0.4458.output.1.0.txt\n", + "5809 valid, 0 skipped, 111 invalid, 53 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/baseline_640_1_epoch12.50_0.4365.output.1.0.txt\n", + "5720 valid, 0 skipped, 123 invalid, 47 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-25_128_1_epoch50.00_0.4780.output.1.0.txt\n", + "5889 valid, 0 skipped, 54 invalid, 14 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-25_128_2_epoch50.00_0.4823.output.1.0.txt\n", + "5935 valid, 0 skipped, 45 invalid, 10 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-25_256_1_epoch50.00_0.4000.output.1.0.txt\n", + "6035 valid, 0 skipped, 25 invalid, 8 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-25_256_2_epoch41.67_0.4244.output.1.0.txt\n", + "6184 valid, 0 skipped, 41 invalid, 7 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/baseline_640_2_epoch12.50_0.4406.output.1.0.txt\n", + "5762 valid, 0 skipped, 119 invalid, 30 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-50_256_2_epoch50.00_0.4460.output.1.0.txt\n", + "6046 valid, 0 skipped, 18 invalid, 11 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-25_384_1_epoch41.67_0.3870.output.1.0.txt\n", + "6052 valid, 0 skipped, 29 invalid, 10 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-25_384_2_epoch33.33_0.3991.output.1.0.txt\n", + "6151 valid, 0 skipped, 17 invalid, 11 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-25_512_1_epoch25.00_0.3859.output.1.0.txt\n", + "5896 valid, 0 skipped, 23 invalid, 8 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-25_512_2_epoch25.00_0.3894.output.1.0.txt\n", + "6057 valid, 0 skipped, 44 invalid, 10 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-25_640_1_epoch20.83_0.3864.output.1.0.txt\n", + "5951 valid, 0 skipped, 31 invalid, 24 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-25_640_2_epoch25.00_0.3864.output.1.0.txt\n", + "5776 valid, 0 skipped, 34 invalid, 7 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-50_128_1_epoch50.00_0.5749.output.1.0.txt\n", + "6097 valid, 0 skipped, 109 invalid, 22 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-50_128_2_epoch50.00_0.5740.output.1.0.txt\n", + "6026 valid, 0 skipped, 50 invalid, 19 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-50_256_1_epoch50.00_0.4398.output.1.0.txt\n", + "5932 valid, 0 skipped, 13 invalid, 2 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-50_384_1_epoch50.00_0.3946.output.1.0.txt\n", + "5958 valid, 0 skipped, 12 invalid, 5 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-50_384_2_epoch50.00_0.3981.output.1.0.txt\n", + "6042 valid, 0 skipped, 16 invalid, 8 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-50_512_1_epoch50.00_0.3835.output.1.0.txt\n", + "6016 valid, 0 skipped, 21 invalid, 8 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-50_512_2_epoch50.00_0.3825.output.1.0.txt\n", + "5918 valid, 0 skipped, 11 invalid, 1 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-50_640_1_epoch20.83_0.4714.output.1.0.txt\n", + "6048 valid, 0 skipped, 18 invalid, 13 failed to parse.\n", + "Opening encoded card file: /data/collected/char-rnn-sweep1/dropout-50_640_2_epoch50.00_0.3805.output.1.0.txt\n", + "5958 valid, 0 skipped, 9 invalid, 2 failed to parse.\n", + "Done gathering data.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/bill/anaconda/lib/python2.7/site-packages/numpy/core/_methods.py:59: RuntimeWarning: Mean of empty slice.\n", + " warnings.warn(\"Mean of empty slice.\", RuntimeWarning)\n", + "/home/bill/anaconda/lib/python2.7/site-packages/numpy/core/_methods.py:70: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n" + ] + } + ], + "source": [ + "# ipython hack, as we don't seem to know what __file__ is\n", + "mtgencodedir = '/home/mtgencode' # might want to make a symlink\n", + "\n", + "import sys\n", + "import os\n", + "from collections import OrderedDict\n", + "libdir = os.path.join(mtgencodedir, 'lib')\n", + "sys.path.append(libdir)\n", + "scriptdir = os.path.join(mtgencodedir, 'scripts')\n", + "sys.path.append(scriptdir)\n", + "datadir = os.path.join(mtgencodedir, 'data')\n", + "import jdecode\n", + "import ngrams\n", + "import analysis\n", + "\n", + "realcards = jdecode.mtg_open_file(str(os.path.join(datadir, 'output.txt')), verbose=True)\n", + "separate_lines = True\n", + "lm = ngrams.build_ngram_model(realcards, n=3, separate_lines=separate_lines, verbose=True)\n", + "\n", + "mtg_sweep1_dir = '/data/collected/mtg-rnn-sweep1'\n", + "char_sweep1_dir = '/data/collected/char-rnn-sweep1'\n", + "\n", + "def sweep_outputs(sweepdir):\n", + " if not os.path.isdir(sweepdir):\n", + " print('not a directory: ' + sweepdir)\n", + " return []\n", + " else:\n", + " return [fname for fname in os.listdir(sweepdir) if os.path.isfile(os.path.join(sweepdir, fname))\n", + " and fname[-4:] == '.txt']\n", + "\n", + "def setup_statistics(sweepdir):\n", + " cps = sweep_outputs(sweepdir)\n", + " all_stats = OrderedDict()\n", + " for cp in cps:\n", + " cp_stats = analysis.get_statistics(os.path.join(sweepdir, cp), lm=lm, sep=separate_lines, verbose=True)\n", + " if 'cp' in cp_stats and 'name' in cp_stats['cp']:\n", + " cp_name = cp_stats['cp']['name']\n", + " else:\n", + " cp_name = cp\n", + " all_stats[cp_name] = cp_stats\n", + " return all_stats\n", + "\n", + "mtg_sweep1 = setup_statistics(mtg_sweep1_dir)\n", + "char_sweep1 = setup_statistics(char_sweep1_dir)\n", + "\n", + "print 'Done gathering data.'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import matplotlib.pyplot as plt\n", + "\n", + "t0 = 1\n", + "t1 = 3\n", + "xs = [2,2,2,1,1,2,3]\n", + "bins = 3\n", + "\n", + "fig, ax = plt.subplots(figsize=(20,5))\n", + "fig2, ax2 = plt.subplots(figsize=(10,10))\n", + "ax.hist(xs, bins=bins, range=(t0,t1), histtype='barstacked', stacked=True, align='left')\n", + "ax.autoscale_view()\n", + "ax.set_ylabel('worktime [h]')\n", + "ax.legend([\"a\"], loc=\"best\")\n", + "fig.autofmt_xdate()\n", + "\n", + "ax2.hist(xs, bins=bins, range=(t0,t1), histtype='barstacked', stacked=True, align='left')\n", + "ax2.autoscale_view()\n", + "ax2.set_ylabel('tim [h]')\n", + "ax2.legend([\"a\"], loc=\"best\")\n", + "fig2.autofmt_xdate()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\nAAAASUVORK5CYII=\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#analysis.print_statistics(mtg_sweep1['baseline_128_1'])\n", + "%matplotlib inline\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "def size_from_name(cp_name):\n", + " return int(cp_name.split('_')[-2])\n", + "\n", + "mtg_sizes = [size_from_name(cp) for cp in mtg_sweep1 if 'baseline' in cp]\n", + "char_sizes = [size_from_name(cp) for cp in char_sweep1 if 'baseline' in cp]\n", + "\n", + "mtg_epochs = [mtg_sweep1[cp]['cp']['epoch'] for cp in mtg_sweep1 if 'baseline' in cp]\n", + "char_epochs = [char_sweep1[cp]['cp']['epoch'] for cp in char_sweep1 if 'baseline' in cp]\n", + "\n", + "mtg_vlosses = [mtg_sweep1[cp]['cp']['vloss'] for cp in mtg_sweep1 if 'baseline' in cp]\n", + "char_vlosses = [char_sweep1[cp]['cp']['vloss'] for cp in char_sweep1 if 'baseline' in cp]\n", + "\n", + "#fig, ax = plt.subplots(figsize=(20,10))\n", + "#ax.plot(mtg_sizes, mtg_epochs, 'bs', char_sizes, char_epochs, 'g^')\n", + "#fig.show()\n", + "\n", + "plt.plot(mtg_sizes, mtg_epochs, 'bs', char_sizes, char_epochs, 'g^')\n", + "plt.show()\n", + "\n", + "plt.plot(mtg_sizes, mtg_vlosses, 'bs', char_sizes, char_vlosses, 'g^')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/scripts/analysis.py b/scripts/analysis.py index 98de19d..3110326 100755 --- a/scripts/analysis.py +++ b/scripts/analysis.py @@ -8,9 +8,14 @@ from collections import OrderedDict import scipy import scipy.stats import numpy as np +import math + +def mean_nonan(l): + filtered = [x for x in l if not math.isnan(x)] + return np.mean(filtered) def gmean_nonzero(l): - filtered = [x for x in l if x != 0] + filtered = [x for x in l if x != 0 and not math.isnan(x)] return scipy.stats.gmean(filtered) libdir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../lib') @@ -18,7 +23,7 @@ sys.path.append(libdir) datadir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../data') import jdecode -import validate +import mtg_validate import ngrams def annotate_values(values): @@ -66,7 +71,7 @@ def get_statistics(fname, lm = None, sep = False, verbose=False): # validate ((total_all, total_good, total_bad, total_uncovered), - values) = validate.process_props(cards) + values) = mtg_validate.process_props(cards) stats['props'] = annotate_values(values) stats['props']['overall'] = OrderedDict([('total', total_all), @@ -97,8 +102,8 @@ def get_statistics(fname, lm = None, sep = False, verbose=False): if cdist == 1.0: card_dupes += 1 - dists['name_mean'] = np.mean(dists['name']) - dists['cbow_mean'] = np.mean(dists['cbow']) + dists['name_mean'] = mean_nonan(dists['name']) + dists['cbow_mean'] = mean_nonan(dists['cbow']) dists['name_geomean'] = gmean_nonzero(dists['name']) dists['cbow_geomean'] = gmean_nonzero(dists['cbow']) stats['dists'] = dists @@ -125,19 +130,20 @@ def get_statistics(fname, lm = None, sep = False, verbose=False): ngram['perp'] += [perp] ngram['perp_per'] += [perp_per] - ngram['perp_mean'] = np.mean(ngram['perp']) - ngram['perp_per_mean'] = np.mean(ngram['perp_per']) + ngram['perp_mean'] = mean_nonan(ngram['perp']) + ngram['perp_per_mean'] = mean_nonan(ngram['perp_per']) ngram['perp_geomean'] = gmean_nonzero(ngram['perp']) ngram['perp_per_geomean'] = gmean_nonzero(ngram['perp_per']) stats['ngram'] = ngram - print_statistics(stats) + return stats def main(infile, verbose = False): lm = ngrams.build_ngram_model(jdecode.mtg_open_file(str(os.path.join(datadir, 'output.txt'))), 3, separate_lines=True, verbose=True) - get_statistics(infile, lm=lm, sep=True, verbose=verbose) + stats = get_statistics(infile, lm=lm, sep=True, verbose=verbose) + print_statistics(stats) if __name__ == '__main__': diff --git a/scripts/validate.py b/scripts/mtg_validate.py similarity index 100% rename from scripts/validate.py rename to scripts/mtg_validate.py diff --git a/scripts/pairing.py b/scripts/pairing.py new file mode 100755 index 0000000..d9f2727 --- /dev/null +++ b/scripts/pairing.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +import sys +import os + +libdir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../lib') +sys.path.append(libdir) +datadir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../data') +import jdecode +import ngrams +import analysis + +separate_lines=True + +def main(fname, n=20, verbose=False): + realcards = jdecode.mtg_open_file(str(os.path.join(datadir, 'output.txt')), verbose=verbose) + lm = ngrams.build_ngram_model(realcards, 3, separate_lines=separate_lines, verbose=verbose) + cards = jdecode.mtg_open_file(fname, verbose=verbose) + stats = analysis.get_statistics(fname, lm=lm, sep=separate_lines, verbose=verbose) + + print 'derp' + +if __name__ == '__main__': + + import argparse + parser = argparse.ArgumentParser() + + parser.add_argument('infile', #nargs='?'. default=None, + help='encoded card file or json corpus to process') + parser.add_argument('-n', '--n', action='store', + help='number of cards to consider for each pairing') + parser.add_argument('-v', '--verbose', action='store_true', + help='verbose output') + + args = parser.parse_args() + main(args.infile, n=args.n, verbose=args.verbose) + exit(0)