Récemment, j'aime jouer avec ma famille sur le site de mahjong en ligne Tenhou une fois par semaine pendant environ 2 heures.
Cette fois, j'expliquerai l'analyse des résultats du concours afin que tout le monde dans la famille puisse le voir sur le web.
En tant que processus:
Fini comme ça ↓ https://drmahjong.herokuapp.com/
Le code peut être trouvé sur le Github ci-dessous. https://github.com/mottoki/mahjan-score
Les données Tenho peuvent être extraites de Log. Prenez les données à l'aide d'un module appelé request.
python.py
import requests
import datetime
new_date = datetime.datetime.now().strftime('%Y%m%d')
url = "https://tenhou.net/sc/raw/dat/"+f"sca{new_date}.log.gz"
filename = f"sca{new_date}.log.gz"
# Download gz file from the url
with open(filename, "wb") as f:
r = requests.get(url)
f.write(r.content)
Filtrez les données brutes par nom de joueur et utilisez la division pour extraire les cadres de données avec uniquement le nom et les points du joueur.
new_data.py
import os
import pickle
import pandas as pd
#Nom de joueur
playercol = ['date', 'Mirataro', 'Shinwan', 'ToShiroh', 'yukoron']
#Convertir en trame de données Pandas
df = pd.read_csv(filename, usecols=[0], error_bad_lines=False, header=None)
df[len(df.columns)] = new_date
#Filtrer par nom de joueur
df = df[(df[0].str.contains(playercol[1])) &
(df[0].str.contains(playercol[2])) &
(df[0].str.contains(playercol[3])) &
(df[0].str.contains(playercol[4]))]
#Traiter la trame de données
df[['one','two','three','four']] = df[0].str.split('|', 3, expand=True)
df.columns = ['original', 'date', 'room', 'time', 'type', 'name1']
df['date'] = pd.to_datetime(df['date'], format='%Y%m%d')
df[['empty', 'n1', 'n2', 'n3', 'n4']] = df.name1.str.split(" ", n=4, expand=True)
#N'utilisez que des colonnes importantes
df = df[['date', 'n1', 'n2', 'n3', 'n4']]
#Supprimez les crochets clés attachés à la partition et au nom pour créer un bloc de données
new_score = pd.DataFrame(columns=playercol)
k=0
for i, j in df.iterrows():
dd = j[0]
new_score.loc[k, 'date'] = dd
for name in df.columns[1:]:
s = j[name]
player = s.split('(')[0]
score = [p.split(')')[0] for p in s.split('(') if ')' in p][0]
score = int(float(score.replace('+', '')))
new_score.loc[k, player] = score
k += 1
#Appeler les anciennes données de Pickle
current_dir = os.getcwd()
old_score = pd.read_pickle(f"{current_dir}/players_score.pkl")
#Combinez les nouvelles et anciennes données
concat_score = pd.concat([old_score, new_score], ignore_index=True)
concat_score.to_pickle(f"{current_dir}/players_score.pkl")
Utilisez une bibliothèque appelée Dash pour visualiser rapidement les données.
Le didacticiel Dash est le plus simple à comprendre. (Référence: Documentation et guide de l'utilisateur Dash)
La partie qui est interceptée dans Dash est la fonction appelée Callback, mais il y a des gens qui l'expliquent en détail comme Utiliser la bibliothèque de visualisation Python Dash 2 See Callback. Veuillez vous y référer.
Expliquer tout le code serait long, je vais donc expliquer la partie principale à titre d'exemple.
En gros, tout ce que vous voyez dans le premier «app.layout» est ce que vous voyez sur votre site Web.
Si vous entrez style = {'display': 'none'}
, vous pouvez masquer les choses que vous ne voulez pas afficher (par exemple, les données que vous utilisez plusieurs fois "valeurs intermédiaires") sur le Web.
#Écrivez le front-end dans ce
app.layout = html.Div([
#Permettre aux utilisateurs de choisir la date qui sera reflétée dans les données
html.Div([
html.H2("DR.Mahjong"),
dcc.DatePickerRange(
id='my-date-picker-range',
min_date_allowed=dt(2020, 3, 1),
max_date_allowed=dt.today(),
end_date=dt.today()
),
], className="mytablestyle"),
#Données utilisées plusieurs fois:style={'display': 'none'}Rends-le invisible
html.Div(id='intermediate-value', style={'display': 'none'}),
#Transition de points (graphique)
dcc.Graph(id='mygraph'),
#Points complets (tableau)
html.Div([
html.Div(html.P('Total actuel des points')),
html.Div(id='totalscore'),
], className="mytablestyle"),
])
Lisez les données avec pandas read_pickle, filtrez par date, jsonize et return.
Cela vous permettra d'utiliser les mêmes données encore et encore dans les graphiques et les tableaux.
@app.callback(Output("intermediate-value", "children"),
[Input("my-date-picker-range", "start_date"),
Input("my-date-picker-range", "end_date")])
def update_output(start_date, end_date):
players = pd.read_pickle('players_score.pkl')
if start_date is not None:
start_date = dt.strptime(re.split('T| ', start_date)[0], '%Y-%m-%d')
players = players.loc[(players['date'] >= start_date)]
if end_date is not None:
end_date = dt.strptime(re.split('T| ', end_date)[0], '%Y-%m-%d')
players = players.loc[(players['date'] <= end_date)]
return players.to_json(date_format='iso', orient='split')
Les données jsonisées sont renvoyées à la trame de données Pandas et représentées graphiquement et tabulées.
Le graphique reprend le style Plotly et est représenté par go.Figure ().
La tableisation est représentée par html.Table. Il existe également une bibliothèque appelée dash_table pour les tables, mais cette fois, la table était simple, j'ai donc décidé d'utiliser ce style car je n'en avais pas besoin.
@app.callback([Output('mygraph', 'figure'),
Output('totalscore', 'children')],
[Input('intermediate-value', 'children'),
Input('datatype', 'value')])
def update_fig(jsonified_df, data_type):
#Restaurez les données Jsonized sur Pandas.
players = pd.read_json(jsonified_df, orient='split')
#Graphisme
fig = go.Figure()
for i, name in enumerate(players.columns[1:]):
fig.add_trace(go.Scatter(x=players.date,
y=np.array(players[name]).cumsum(),
mode='lines',
name=name,
line=dict(color=colors[i], width=4)))
fig.update_layout(plot_bgcolor='whitesmoke',
title='Transition du total des points',
legend=dict(
orientation="h",
yanchor="bottom",
y=1.02,
xanchor="right",
x=1,)
)
#Calculer le total des points
summed = players.sum()
#Renvoie des graphiques et des tableaux
return fig, html.Table([
html.Thead(
html.Tr([html.Th(col) for col in summed.index])
),
html.Tbody(
html.Tr([html.Td(val) for val in summed])
),
])
Enfin, nous déploierons en utilisant Heroku et Github.
Le site Web officiel (Deploying Dash Apps) contient des instructions détaillées sur l'utilisation de Git et Heroku, les méthodes sont donc presque les mêmes.
Le processus ressemble à ceci:
Créez un compte Github
Créez un nouveau référentiel Github
SSH sur Github. (Facultatif, mais plus simple à faire. Voir: Autoriser la connexion ssh à GitHub)
Créez les fichiers requis pour le déploiement (.ignore
, Procfile
, requirements.txt
). Vous avez également besoin de gunicorn, alors installez-le avec pip install gunicorn
.
Utilisez la commande Git pour envoyer le fichier ci-dessus et les fichiers de données de ʻapp.pyet de
players_score.pkl` vers Github.
git init
git add .
git commit -m "message"
git remote add origin [email protected]:<Nom d'utilisateur>/<Nom du référentiel>
git push origin master
Après avoir confirmé qu'il a été envoyé sur Github, créez un compte Heroku et créez une nouvelle application avec le bouton Nouveau> Créer une nouvelle application
(la région n'a pas le Japon, alors sélectionnez États-Unis).
Cliquez sur l'onglet Déployer de l'application créée, définissez Méthode de déploiement
sur Github et connectez-vous au référentiel créé dans 2.
Enfin, appuyez sur le bouton noir «Deploy Branch» dans «Manual deploy» pour le déployer sans autorisation.
Comment était-ce?
Vous pouvez également utiliser cron et le déploiement automatique de Heroku pour automatiser la mise à jour des nouvelles données de Tenho. (Référence: Automatiser le processus de poussée vers Github avec cron)
Recommended Posts