2020/11/12

Pythonでロト6のデータをやんわり解析してみる

Pandasはやっぱり便利

私、久々にPythonを使う気がします。Pandasはナンバーズ4のデータを解析して以来となります。久々に使ってみても、やはり便利なライブラリだな、と感じます。

今回、ロト6の全データを入手することができたので解析をやろうというわけなのですが、以前はBeautifulSoup4を使って、みずほ銀行のロト6のページをスクレイピングして、Pandasを使うということをやり、なかなか骨の折れる作業(最近のデータと過去データのHTMLタグが統一的でない)だったのに、結果が伴わなかったことでロト6から遠ざかっていました。偶然1等1回当たれば満足なのに……

今回は有志の方のマメさとおおらかな心のおかげで全データのExcelファイルを入手できました。おかげでスクレイピングの手間が省けて、文字コードをShift-JISからUTF-8に変換するくらいの手間ですぐに解析にかかることができました。あとは最新の結果を自身でちょっとずつ追加していけば、常にデータを最新に保つことができる。

また、ここ最近Pythonではpoetryというプロジェクト管理ツールが流行って来ているらしいので、そちらを試してみたかったというのもあります。poetryの使い方については公式ドキュメントを参考にしてください。

ということで、早速作業に取り掛かります。

loto6

今回使うのはPython3.8、Pandas、xlrdです。


$ poetry new loto6
$ cd loto6
$ poetry add pandas
$ poetry add xlrd

そして入手したExcelファイルloto6.xlsxをloto6ディレクトリに置く。すると以下のような構成になります。


loto6/
    loto6/
        __init__.py
    tests/
        __init__.py
        test_loto6.py
    loto6.xlsx
    poetry.lock
    pyproject.toml
    README.rst
    

loto6ディレクトリにmain.pyを作成。これにいろいろ書いていこうと考えています。

欲しい機能は以下のようなもの

  • 全データ閲覧
  • 過去データの閲覧(件数指定)
  • 各桁ごとの数字の出現頻度
  • 各桁の数字の相性
  • 自分が予想した数字の組み合わせが過去に出現したことがあるかどうか

やんわり解析なので、これぐらいで十分かなと思っています。

そして、それらのコードは以下のようになりました。


import pandas
from collections import Counter

class Loto:
    _LOTO_DATA = pandas.read_excel('./loto6.xlsx')
    D1 = _LOTO_DATA['第1数字']
    D2 = _LOTO_DATA['第2数字']
    D3 = _LOTO_DATA['第3数字']
    D4 = _LOTO_DATA['第4数字']
    D5 = _LOTO_DATA['第5数字']
    D6 = _LOTO_DATA['第6数字']
    DB = _LOTO_DATA['BONUS数字']

    def data_all(self):
        return self._LOTO_DATA

    def data_head(self, row):
        return self._LOTO_DATA.head(row)

    def data_tail(self, row):
        return self._LOTO_DATA.tail(row)

    def freq(self, data):
        cnt = Counter(data).most_common()
        return cnt

    def freq_pattern(self, *args):
        ''' e.g.)
        >>> l = Loto()
        >>> l.freq_pattern(l.D1, l.D2)
        '''
        cnt = Counter(zip(*args)).most_common()
        return cnt

    def existp(self, *expected):
        data = self.freq_pattern(self.D1,
                                 self.D2,
                                 self.D3,
                                 self.D4,
                                 self.D5,
                                 self.D6)
        for pt, _ in data:
            if pt == expected:
                return True
            else:
                continue

        return False

データファイルがあればこんなに簡潔に書くことができるなんて!

もはや当たる気しかしない……

話は変わりますが……

先日歯医者に行った際、ついでに書店に寄ったところ、面白いPythonの本を見つけたのでご紹介しておきたいと思います。

まさか家で動脈硬化のチェックができるなんて!しかもスマホとPythonがあれば。といったおもしろアイデアが詰まった内容になっておりました。皆様もぜひ読んでみてください。

追記(2020/01/13)

Python3.9にアップデートし、再びpandasを使おうとしたところ、以前までxlrdで読み込みできていたExcelファイルでしたが、現在はxlrdではなく、openpyxlを使うようになったようです。そしてopenpyxlを使うと、私が使っていましたデータは浮動小数点数となり、空白部分がNaN(Not a number)となりましたので、新たにプログラムを以下のように書き直しました。


import pandas
from collections import Counter


class Loto:
    BASE_DATA = pandas.read_excel('loto6.xlsx').dropna().astype(int)
    KEY_DATA = BASE_DATA.iloc[:, 2:]

    D1 = KEY_DATA['第1数字']
    D2 = KEY_DATA['第2数字']
    D3 = KEY_DATA['第3数字']
    D4 = KEY_DATA['第4数字']
    D5 = KEY_DATA['第5数字']
    D6 = KEY_DATA['第6数字']
    DB = KEY_DATA['BONUS数字']

    def freq(self, data):
        return Counter(data).most_common()

    def freq_pattern(self, *args):
        return Counter(zip(*args)).most_common()

    def freq_all(self, recent_row=None):
        _all_freq = self.freq_pattern(
                        self.D1,
                        self.D2,
                        self.D3,
                        self.D4,
                        self.D5,
                        self.D6,
                        self.DB
                    )
        if recent_row is None:
            return _all_freq
        else:
            if isinstance(recent_row, int):
                return _all_freq[::-1][:recent_row]
            else:
                raise ValueError("recent_row must be int.")

    def find(self, col, num):
        return self.KEY_DATA[col == num]

dropna関数はNaNを非表示にすることができ、astype(int)とすることで、浮動小数点数だったデータを整数値にすることができます。そして今回は開催回数や日付を無視するためにiloc[:, 2:]とし、数値データだけにしたKEY_DATAというものを作り、扱うようにしました。もちろん当選はしていない……