CoderCastrov logo
CoderCastrov
Наука о данных

Анализ сложности игры Wordle с использованием твитов: Часть 2

Анализ сложности игры Wordle с использованием твитов: Часть 2
просмотров
3 мин чтение
#Наука о данных

Пожалуйста, найдите первую часть этой работы здесь. Исходя из проделанной работы в предыдущей части, целью является разработка Flask-приложения и его размещение на локальном хосте с ожиданием размещения на AWS.

Ввод

Вводом для приложения будет дата. Здесь мы используем следующий html-файл в папке templates нашего приложения. Мы используем простую форму для получения даты в качестве ввода для нашего Flask-приложения.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Главная страница</title>
</head>
    
<body>
<h1>
        Добро пожаловать в приложение для анализа сложности игры Wordle
    </h1>
    Пожалуйста, выберите дату, которую вы хотите проанализировать, из следующего списка.
<form action="{{ url_for('predict') }}" method="post">            
<input type='date' name='date' min='2022-11-24'></input>
<input type="submit" value="Отправить">
    </form>
</body>
</html>

Flask приложение

Ниже приведен полный код для Flask приложения. Здесь мы сначала проверяем, доступны ли данные для запрошенной даты в нашем dataframe. Если данные доступны, мы сгенерируем простую html форму, используя строки на языке Python, и вернем ее. Если запрошенная дата является будущей датой, мы покажем сообщение об ошибке. Если запрошенная дата является прошедшим днем, и данные отсутствуют в нашем dataframe, мы вызовем фреймворк Twint для парсинга новых данных. Эти новые данные будут сохранены в нашем dataframe для будущего использования.

# Импорт библиотек
import twint
import nest_asyncio
import numpy as np
import pandas as pd
from flask import Flask, request, jsonify, render_template
from datetime import date,timedelta,datetime
import re

app = Flask(__name__)
nest_asyncio.apply()
# Загрузка данных
file_path = 'Tweets_Wordle_Data.csv'
tweets = pd.read_csv(file_path)
tweets['Date']= pd.to_datetime(tweets['Date']).dt.date
tweets=tweets.drop(columns="Unnamed: 0")
tweets = tweets.set_index('Date')
wordleRegex = re.compile(r'Wordle \d\d\d [\dX]/6')

@app.route('/')
def home_endpoint():
    return render_template("index.html")

@app.route('/api',methods=['POST'])
def predict():
    # Получение данных из POST запроса.
    date = request.form.get("date")
    date=datetime.strptime(date,"%Y-%m-%d").date()
    
    print(date)
    print(type(date))
    #most_recent_date = tweets['Date'].max()
    #print(type(most_recent_date))
    #print(tweets['Date'])
    vals=getDataForDate(date)
    if type(vals)!=str:
        ans=f"<h1>Отображение сложности Wordle для {str(date)}</h1>"
        ans+=f"<table><tr><td>Успешность</td><td>{vals['Success']*100:.2f}%</td></tr>"
        ans+=f"<tr><td>Среднее количество попыток</td><td>{vals['Average']:.2f}</td></tr>"
        ans+=f"<tr><td>Процентиль сложности</td><td>{vals['Percentile Rank']:.2f}%</td></tr></table><br/>"
        ans+=getDifficultyLevel(vals['Percentile Rank'])
        ans+="Распределение попыток следующее <br>"
        ans+=genTable(vals)
    else:
        ans=vals;
    return ans    
        
def genTable(vals):
    result="<table><tr><th>Попытки</th><th>Процент</th></tr>"
    fields=['1','2','3','4','5','6','X']
    for i in fields:
        result+=f"<tr><td>{i}</td><td>{vals[i]:.1f}</td></tr>"
    result+="</table>"
    return result

def getDataForDate(date):
    if date>date.today():
        return 'Извините. Данные для будущих дат недоступны.'
    elif (date in tweets.index):
        return tweets.loc[date]
    else:
        scrapeData()
        return 'Извините. Данные для этого дня недоступны. Пожалуйста, попробуйте снова через несколько минут. Мы парсим эти данные.'

def getDifficultyLevel(percentile):
    if percentile>80:
        return '<p style="color:red;">Это было очень сложное слово.</p>'
    elif percentile>60:
        return '<p style="color:pink;">Это было сложное слово.</p>'
    elif percentile>40:
        return '<p style="color:orange;">Это было средней сложности слово.</p>'
    elif percentile>80:
        return '<p style="color:blue;">Это было легкое слово.</p>'
    else:
        return '<p style="color:green;">Это было очень легкое слово.</p>'
        
        
def scrapeData():
    file_path = 'Tweets_Wordle_Data.csv'
    tweets = pd.read_csv(file_path)
    tweets['Date']= pd.to_datetime(tweets['Date'])
    tweets=tweets.drop(columns="Unnamed: 0")
    most_recent_date = tweets['Date'].max()
    date_to_scrape=most_recent_date+ timedelta(days=1) 
    while(date_to_scrape<=date.today()):
        temp={}
        startDay = date_to_scrape.strftime("%Y-%m-%d")+' 00:00:00'
        endDay = date_to_scrape.strftime("%Y-%m-%d")+' 23:59:00'
        print(f'Начальная дата={startDay} конечная дата={endDay}')
        tweets_df=scrapePerDay(startDay,endDay)
        tweets_df=processTweets(tweets_df)
        temp['Date']=startDay
        ans=tweets_df.attempts.value_counts().sort_values()
        fields=['1','2','3','4','5','6','X']
        for key in fields:
            if key in ans.index:
                temp[key]=ans.loc[key]
            else:
                temp[key]=0
        temp['Num tweets']=len(tweets_df)

        temp['Success']=(temp['Num tweets']-temp['X'])/temp['Num tweets']
        temp['Average']=(temp['1']*1+temp['2']*2+temp['3']*3+temp['4']*4+temp['5']*5+temp['6']*6)/(temp['Num tweets']-temp['X'])
        temp['Percentile Rank'] = 0
        for i in fields:
            temp[i]=temp[i]*100/temp['Num tweets']
        tweets=tweets.append(temp,ignore_index=True)
        date_to_scrape=date_to_scrape+ timedelta(days=1) 
    tweets['Percentile Rank'] = tweets.Average.rank(pct = True)*100
    with open(file_path, 'w') as f:
        tweets.to_csv(f)
    tweets = tweets.set_index('Date')

def scrapePerDay(startDay,endDay):
    c = twint.Config()   
    c.Since = startDay
    c.Until = endDay
    c.Pandas = True
    c.Search = "Wordle"  # ключевые слова для поиска.
    twint.run.Search(c)
    tweets_df = twint.storage.panda.Tweets_df
    return tweets_df

def processTweets(tweets_df):
    tweets=tweets_df[['tweet']]
    tweets['Wordle']=tweets.tweet.apply(findWordleReg)
    tweets=tweets[tweets['Wordle']!='None']
    tweets['version']=tweets.Wordle.str[7:10]
    tweets['attempts']=tweets.Wordle.str[-3]
    return tweets

def findWordleReg(tweet_ans):
    mo=wordleRegex.search(tweet_ans)
    if mo!=None:
        return mo.group()
    else:
        return 'None'
    
if __name__ == '__main__':
    app.run(port=5000, debug=True)

Результаты

Приложение Flask возвращает процент успешности Wordle для выбранной даты, среднее количество попыток для решения головоломки и относительный уровень сложности. Также оно показывает распределение попыток для конкретной даты.

Результаты, возвращаемые приложением Flask для 8 декабря 2022 года.

Вывод

В этой работе мы использовали ранее настроенный Twint и вспомогательный код для создания приложения Flask и простого веб-интерфейса для анализа сложности головоломки Wordle для каждого дня. Код и база данных с 24 ноября 2022 года по 12 декабря 2022 года доступны здесь. Следующим шагом этого проекта будет развертывание приложения Flask на AWS, чтобы любой пользователь мог получить статистику сложности головоломки Wordle для любой доступной даты в системе.