Skip to content

Instantly share code, notes, and snippets.

@kun432
Last active January 16, 2023 12:08
Show Gist options
  • Save kun432/07b1df826819d8938c51b1df2e96611a to your computer and use it in GitHub Desktop.
Save kun432/07b1df826819d8938c51b1df2e96611a to your computer and use it in GitHub Desktop.
隊列パースconcat版
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"authorship_tag": "ABX9TyPCVTB3RATGHAn9Gv4Ne7d1",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/kun432/07b1df826819d8938c51b1df2e96611a/-concat.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"source": [
"zennの記事を元に以下を追加してみた。\n",
"\n",
"- 各コーナーでの内外の位置取り\n",
"- matplotlibでかんたんな可視化\n",
"- pandas-1.4.0でappendが廃止されることに伴い、concatに書き換え\n",
"\n",
"参考)pyparsingで競馬のコーナー通過順位をパース\n",
"https://zenn.dev/moripon/articles/ed5caa9c1d621e\n"
],
"metadata": {
"id": "qngtoogbjC2m"
}
},
{
"cell_type": "code",
"source": [
"!pip install japanize-matplotlib\n",
"!pip install 'pandas>1.4.0'"
],
"metadata": {
"id": "46uGuY2ObEWu",
"colab": {
"base_uri": "https://localhost:8080/"
},
"outputId": "7d3daaa6-4336-42bb-999c-e1133f51b5c7"
},
"execution_count": 1,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n",
"Collecting japanize-matplotlib\n",
" Downloading japanize-matplotlib-1.1.3.tar.gz (4.1 MB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m4.1/4.1 MB\u001b[0m \u001b[31m27.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
"Requirement already satisfied: matplotlib in /usr/local/lib/python3.8/dist-packages (from japanize-matplotlib) (3.2.2)\n",
"Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.8/dist-packages (from matplotlib->japanize-matplotlib) (0.11.0)\n",
"Requirement already satisfied: numpy>=1.11 in /usr/local/lib/python3.8/dist-packages (from matplotlib->japanize-matplotlib) (1.21.6)\n",
"Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.8/dist-packages (from matplotlib->japanize-matplotlib) (1.4.4)\n",
"Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.8/dist-packages (from matplotlib->japanize-matplotlib) (3.0.9)\n",
"Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.8/dist-packages (from matplotlib->japanize-matplotlib) (2.8.2)\n",
"Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.8/dist-packages (from python-dateutil>=2.1->matplotlib->japanize-matplotlib) (1.15.0)\n",
"Building wheels for collected packages: japanize-matplotlib\n",
" Building wheel for japanize-matplotlib (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
" Created wheel for japanize-matplotlib: filename=japanize_matplotlib-1.1.3-py3-none-any.whl size=4120275 sha256=f29c5e94197668dc3230867b591f20bbf976b35ebf916b975309857672192dea\n",
" Stored in directory: /root/.cache/pip/wheels/4f/ca/96/4cc5e192421cceb077fbf4ffec533382edd416fd3fa0af0bbd\n",
"Successfully built japanize-matplotlib\n",
"Installing collected packages: japanize-matplotlib\n",
"Successfully installed japanize-matplotlib-1.1.3\n",
"Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n",
"Collecting pandas>1.4.0\n",
" Downloading pandas-1.5.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (12.2 MB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m12.2/12.2 MB\u001b[0m \u001b[31m19.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hRequirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.8/dist-packages (from pandas>1.4.0) (2022.7)\n",
"Requirement already satisfied: numpy>=1.20.3 in /usr/local/lib/python3.8/dist-packages (from pandas>1.4.0) (1.21.6)\n",
"Requirement already satisfied: python-dateutil>=2.8.1 in /usr/local/lib/python3.8/dist-packages (from pandas>1.4.0) (2.8.2)\n",
"Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.8/dist-packages (from python-dateutil>=2.8.1->pandas>1.4.0) (1.15.0)\n",
"Installing collected packages: pandas\n",
" Attempting uninstall: pandas\n",
" Found existing installation: pandas 1.3.5\n",
" Uninstalling pandas-1.3.5:\n",
" Successfully uninstalled pandas-1.3.5\n",
"Successfully installed pandas-1.5.2\n"
]
}
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"id": "UCipf59VE4EB"
},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"import pyparsing as pp\n",
"import matplotlib.cm as cm\n",
"import matplotlib.pyplot as plt\n",
"import japanize_matplotlib"
]
},
{
"cell_type": "code",
"source": [
"# DataFrame列名定義\n",
"columns = ['diff', 'horse_no', 'side']\n",
"\n",
"# 差の定数(unit:馬身)\n",
"DIFF_GROUP = 0.3\n",
"DIFF_MIN = 1.5\n",
"DIFF_MID = 3.0\n",
"DIFF_MUCH = 6.0\n",
"\n",
"class ParsePass():\n",
" \n",
" def __init__(self):\n",
" \n",
" # 馬番\n",
" horse_no = pp.Word(pp.nums).setParseAction(self._horse_no_action)\n",
" \n",
" # 馬群\n",
" group = pp.Suppress(pp.Literal('(')) + \\\n",
" pp.Optional(pp.delimitedList(pp.Word(pp.nums), delim=',')) + \\\n",
" pp.Suppress(pp.Literal(')'))\n",
" group.ignore('*')\n",
" group.setParseAction(self._group_action)\n",
"\n",
" # 情報要素\n",
" element = (group | horse_no)\n",
" \n",
" # 前走馬との差\n",
" diff_min = pp.Suppress(pp.Optional(pp.Literal(','))).setParseAction(self._diff_min_action) + element\n",
" diff_mid = pp.Suppress(pp.Literal('-')).setParseAction(self._diff_mid_action) + element\n",
" diff_much = pp.Suppress(pp.Literal('=')).setParseAction(self._diff_much_action) + element\n",
"\n",
" # 全体定義\n",
" self._passing_order = element + pp.ZeroOrMore( diff_mid | diff_much | diff_min )\n",
" \n",
" def _horse_no_action(self, token):\n",
" \n",
" df_append = pd.DataFrame(data=[[self._diff, token[0], 1]], columns=columns)\n",
" self._data = pd.concat([self._data, df_append], ignore_index=True, axis=0).drop_duplicates().reset_index(drop=True)\n",
" return\n",
"\n",
" def _group_action(self, token):\n",
" \n",
" for i, no in enumerate(token):\n",
" df_append = pd.DataFrame(data=[[self._diff, no, 1+i]], columns=columns)\n",
" self._data = pd.concat([self._data, df_append], ignore_index=True, axis=0).drop_duplicates().reset_index(drop=True)\n",
" self._diff += DIFF_GROUP\n",
" self._diff -= DIFF_GROUP\n",
" return\n",
" \n",
" def _diff_min_action(self, token):\n",
" \n",
" self._diff += DIFF_MIN\n",
" return\n",
" \n",
" def _diff_mid_action(self, token):\n",
" \n",
" self._diff += DIFF_MID\n",
" return\n",
" \n",
" def _diff_much_action(self, token):\n",
" \n",
" self._diff += DIFF_MUCH\n",
" return\n",
" \n",
" def parse(self, pass_str):\n",
" \n",
" # 初期化\n",
" self._data = pd.DataFrame(columns=columns)\n",
" self._diff = 0\n",
" # parse\n",
" self._passing_order.parseString(pass_str)\n",
" # index調整\n",
" self._data.index = np.arange(1, len(self._data)+1)\n",
" self._data.index.name = 'rank'\n",
"\n",
" return(self._data)"
],
"metadata": {
"id": "mvMLNW17E_0B"
},
"execution_count": 23,
"outputs": []
},
{
"cell_type": "code",
"source": [
"pass_data = '(*12,10,15)5-(1,13)-(2,4)3(6,9)(14,11)(8,7)'#@param {type: \"string\"}"
],
"metadata": {
"id": "KndSiMxMkMCh"
},
"execution_count": 24,
"outputs": []
},
{
"cell_type": "code",
"source": [
"pass_parsing = ParsePass()\n",
"df = pass_parsing.parse(pass_data)\n",
"df"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 551
},
"id": "yV6efWYeFLG7",
"outputId": "eab0e5af-8be6-4ebb-a098-0d0549c018df"
},
"execution_count": 25,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
" diff horse_no side\n",
"rank \n",
"1 0 12 1\n",
"2 0.3 10 2\n",
"3 0.6 15 3\n",
"4 2.1 5 1\n",
"5 5.1 1 1\n",
"6 5.4 13 2\n",
"7 8.4 2 1\n",
"8 8.7 4 2\n",
"9 10.2 3 1\n",
"10 11.7 6 1\n",
"11 12.0 9 2\n",
"12 13.5 14 1\n",
"13 13.8 11 2\n",
"14 15.3 8 1\n",
"15 15.6 7 2"
],
"text/html": [
"\n",
" <div id=\"df-4289b435-d45d-4e6f-98e5-02e0129dd756\">\n",
" <div class=\"colab-df-container\">\n",
" <div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>diff</th>\n",
" <th>horse_no</th>\n",
" <th>side</th>\n",
" </tr>\n",
" <tr>\n",
" <th>rank</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>0</td>\n",
" <td>12</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>0.3</td>\n",
" <td>10</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>0.6</td>\n",
" <td>15</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2.1</td>\n",
" <td>5</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>5.1</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>5.4</td>\n",
" <td>13</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>8.4</td>\n",
" <td>2</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>8.7</td>\n",
" <td>4</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>10.2</td>\n",
" <td>3</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>10</th>\n",
" <td>11.7</td>\n",
" <td>6</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>11</th>\n",
" <td>12.0</td>\n",
" <td>9</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>12</th>\n",
" <td>13.5</td>\n",
" <td>14</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13</th>\n",
" <td>13.8</td>\n",
" <td>11</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14</th>\n",
" <td>15.3</td>\n",
" <td>8</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>15</th>\n",
" <td>15.6</td>\n",
" <td>7</td>\n",
" <td>2</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>\n",
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-4289b435-d45d-4e6f-98e5-02e0129dd756')\"\n",
" title=\"Convert this dataframe to an interactive table.\"\n",
" style=\"display:none;\">\n",
" \n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
" <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
" </svg>\n",
" </button>\n",
" \n",
" <style>\n",
" .colab-df-container {\n",
" display:flex;\n",
" flex-wrap:wrap;\n",
" gap: 12px;\n",
" }\n",
"\n",
" .colab-df-convert {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-convert:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
"\n",
" <script>\n",
" const buttonEl =\n",
" document.querySelector('#df-4289b435-d45d-4e6f-98e5-02e0129dd756 button.colab-df-convert');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" async function convertToInteractive(key) {\n",
" const element = document.querySelector('#df-4289b435-d45d-4e6f-98e5-02e0129dd756');\n",
" const dataTable =\n",
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
" [key], {});\n",
" if (!dataTable) return;\n",
"\n",
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
" + ' to learn more about interactive tables.';\n",
" element.innerHTML = '';\n",
" dataTable['output_type'] = 'display_data';\n",
" await google.colab.output.renderOutput(dataTable, element);\n",
" const docLink = document.createElement('div');\n",
" docLink.innerHTML = docLinkHtml;\n",
" element.appendChild(docLink);\n",
" }\n",
" </script>\n",
" </div>\n",
" </div>\n",
" "
]
},
"metadata": {},
"execution_count": 25
}
]
},
{
"cell_type": "code",
"source": [
"plt.figure(figsize=(df[\"diff\"].max(),df[\"side\"].max()))\n",
"plt.scatter(df[\"diff\"], df[\"side\"], s=100)\n",
"plt.xlabel(\"前 ← 前後 → 後\", fontsize=15)\n",
"plt.ylabel(\"内 ← 内外 → 外\", fontsize=15)\n",
"plt.title(\"隊列\", fontsize=20)\n",
"plt.yticks([0,1,2,3,4], color=\"w\") "
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 357
},
"id": "yac18U0njctj",
"outputId": "eaf7e9a4-89d0-4734-901c-0eb0f3266618"
},
"execution_count": 22,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"([<matplotlib.axis.YTick at 0x7f8b1ac7c190>,\n",
" <matplotlib.axis.YTick at 0x7f8b1ac7c280>,\n",
" <matplotlib.axis.YTick at 0x7f8b1ab239a0>,\n",
" <matplotlib.axis.YTick at 0x7f8b1ab3d310>,\n",
" <matplotlib.axis.YTick at 0x7f8b1ab3d820>],\n",
" <a list of 5 Text major ticklabel objects>)"
]
},
"metadata": {},
"execution_count": 22
},
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 1123.2x216 with 1 Axes>"
],
"image/png": "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\n"
},
"metadata": {}
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment