Часовыя шэрагі: імпарт і ачыстка даных

12 снежня 2023

Катэгорыя: Артыкулы

Зменены: 12.12.2023

Меткі:


Тэма: ІР ІС

Частка 2 з 2.

Папярэднія артыкулы тэмы:

  1. JSON вэб-скрапінг даных

 

Падзяліцца:

Загрузка неабходных бібліятэк

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import seaborn as sns
import os
%matplotlib inline

Вызначэнне ўласных функцый

Функцыя стварэння фрэйма даных часовых шэрагаў

def time_series(df):
    '''Creates time series data frames with random noise'''
    # спрашчэнне назваў слупкоў
    df_cols = ['reg', 'upd', 'exc'] 
    min_df = min(df[df_cols].min()) 
    max_df = max(df[df_cols].max()) + np.timedelta64(1, 'D')
    # стварыць пусты індэкс даты і часу
    df_ts = pd.date_range(start=min_df, end=max_df, freq='D', name='date').to_frame(name='ids') 
    for col in df_cols:
        # стварыць скрыю даты і часу для кожнага слупка
        temp_df = df.groupby(col)['num'].count().to_frame(name=col)
        # аб'яднаць серыі ў адзін фрэйм даных
        df_ts = df_ts.merge(temp_df, how='left', left_index=True, right_index=True)      
    # выдаліць часовы індэкс
    df_ts.drop(['ids'], axis=1, inplace=True) 
    # NaN можа разглядацца зманліва, і ў нашым выпадку нулі маюць большы сэнс
    df_ts[df_cols] = df_ts[df_cols].fillna(0).astype(int)
    # перафарміраваць да месячных даных, у адваротным выпадку гэтых даных недастаткова для вывадаў
    df_ts = df_ts.resample('M').sum() 
    # стварыць выпадковы шум для параўнання
    mean_rand = (int(np.max(df_ts.std())) + 1) 
    # стварыць выпадковы стан для прайграванасці
    np.random.seed(seed=42) 
    df_ts['rand'] = np.random.randint(0, mean_rand, df_ts.shape[0])
    return df_ts

Функцыя стварэння графікаў

def ts_plot(df, kind):
    '''Creates one of three seaborn multi-plots'''
    # melted dataframe лягчэй выкарыстоўваць у seaborn
    melted_df = pd.melt(df, value_vars=['reg', 'upd', 'exc', 'rand'], 
                       var_name='Event', value_name='Number', ignore_index=False)
    melted_df.Event.replace(to_replace={'reg':'Registration', 
                                        'upd':'Update', 
                                        'exc':'Exclusion', 
                                        'rand':'Random Noise'}, inplace=True)
    if kind == 'histogram':
        g = sns.displot(data=melted_df, x='Number', col='Event', 
                        col_wrap=2, hue='Event', kind='hist', 
                        height=3, aspect=2, legend=False)
        g.set_axis_labels('', 'Events Number')
        g.set_titles('{col_name}')
        g.fig.subplots_adjust(top=0.93)
        g.fig.suptitle('Histograms', fontweight='bold')
    elif kind == 'lineplot':
        g = sns.relplot(data=melted_df, x='date', y='Number', 
                        row='Event', hue='Event', kind='line', 
                        height=2, aspect=5, legend=False)
        g.set_axis_labels('', 'Event Numbers')
        g.set_titles('{row_name}')
        g.fig.subplots_adjust(top=0.93)
        g.fig.suptitle('Number of events over time', fontweight='bold')
        for ax in g.axes.flat:
            ax.axhline(y=1, color='gray', linestyle=':')
            # без двух наступных радкоў не ўсе гады пазначаны на восі іксаў
            ax.xaxis.set_major_locator(mdates.YearLocator()) 
            ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
            ax.xaxis.set_tick_params(rotation=45)
    elif kind == 'boxplot':
        g = sns.catplot(data=melted_df, x='Number', y='Event', 
                        kind='box', hue='Event', height=2, 
                        aspect=5.5, legend=False)
        g.set_axis_labels('', '')
        g.fig.subplots_adjust(top=0.93)
        g.fig.suptitle('Boxplots', fontweight='bold')
    else:
        g = 'Няма чаго паказаць. Калі ласка, удакладніце: histogram, lineplot, ці boxplot'
    return g  

Загрузка даных

Загрузка слупкоў з унікальнымі лічбамі і датамі

# спрасціць назвы слупкоў
cols = ['num', 'reg', 'upd', 'exc'] 

# даныя, звязаныя з рэгістрацыяй, абнаўленнем і выключэннем інфармацыйных сістэм
sys_df = pd.read_csv('data/inf_sys.csv', parse_dates=[1,2,3], usecols=['numberOnRegistration', 
                                                                       'dateOnRegistration',
                                                                       'dateActyalization',
                                                                       'dateExclude'])
sys_df.columns = cols

# даныя, звязаныя з рэгістрацыяй, абнаўленнем і выключэннем інфармацыйных рэсурсаў
res_df = pd.read_csv('data/inf_res.csv', parse_dates=[1,2,3], usecols=['numberOnRegistration',
                                                                       'dateOnRegistration',
                                                                       'dateActualization', 
                                                                       'dateExclude'])
res_df.columns = cols

Робім часовы шэраг з неапрацаваных лічбаў

# Фрэйм даных часововага шэрагу інфармацыйных сістэм
sys_df_dt = time_series(sys_df)

# Фрэйм даных часововага шэрагу інфармацыйных рэсурсаў
res_df_dt = time_series(res_df)

Агляд падзей з інфармацыйнымі сістэмамі


Агульная колькасць размеркаванняў падзей

ts_plot(sys_df_dt, 'histogram')
plt.show()

png

Размеркаванне выглядае моцна скажоным, асабліва ў параўнанні з выпадковымі лічбамі.

Падрабязная інфармацыя пра агульную колькасць падзей

ts_plot(sys_df_dt, 'boxplot')
plt.show()

png

Колькасць рэгістрацый мае большую дысперсію, аднак даныя маюць велізарныя выкіды.

# Давайце паглядзім на нашы выкіды
sys_df_dt.reg.sort_values(ascending=False)[:3]
date
2014-08-31    58
2014-09-30    25
2014-07-31    13
Name: reg, dtype: int64

Тройка найбольшых выкідаў - гэта другая палова 2014 года.

Штомесячныя змены падзей у часе

ts_plot(sys_df_dt, 'lineplot')
plt.show()

png

Шэрая пункцірная лінія азначае як мінімум адну падзею ў месяц. Як мы бачым, выключэнне з рэестра або змяненне запісу - даволі рэдкая падзея.

У нас ёсць адна незвычайная вяршыня, якую пазней можа спатрэбіцца зрабіць больш гладкай. Тым не менш, графік выглядае стацыянарным: не дэманструе відавочнай тэндэнцыі і сезоннасці.

Такім чынам, мы можам пабудаваць прагнастычную мадэль. Аднак найбольш перспектыўным з’яўляецца колькасць рэгістрацыі ў месяц.

Давайце паглядзім на іншы фрэйм даных.

Агляд падзей інфармацыйных рэсурсаў


Агульная колькасць размеркаванняў падзей

ts_plot(res_df_dt, 'histogram')
plt.show()

png

Размеркаванне таксама скажонае. Аднак колькасць рэгістрацый больш збалансаваная.

Падрабязная інфармацыя пра агульную колькасць падзей

ts_plot(res_df_dt, 'boxplot')
plt.show()

png

Нягледзячы на выкіды, дысперсія колькасці рэгістрацыі бліжэй да выпадковых цэлых лічбаў.

Штомесячныя змены падзей у часе

ts_plot(res_df_dt, 'lineplot')
plt.show()

png

У нас можа быць тэндэнцыя, аўтакарэляцыя і сезоннасць колькасці рэгістрацый. Колькасць мадыфікацый запісаў стала больш-менш стабільнай з 2018 года. Астатнія даныя ўяўляюць сабой больш рэдкія падзеі.

Заключэнне


У нас дастаткова даных для аналізу часовых шэрагаў рэгістрацыі інфармацыйных сістэм і рэсурсаў. Аднак пажадана разглядаць іх асобна, таму што яны моцна адрозніваюцца па сярэднямесячнай колькасці і даўгаце.

Рэгістрацыя інфармацыйных сістэм выглядае стацыянарнай. Такім чынам, нам можа спатрэбіцца мадэль ARIMA.

Інфармацыйныя рэсурсы могуць мець тэндэнцыю, аўтакарэляцыю і сезоннасць. Вось чаму мы будзем выкарыстоўваць мадэль SARIMA.

Нашым наступным крокам будзе мадэляванне гэтых даных для тлумачэння і прагназавання.

# захаваем змененыя даныя для будучага аналізу
os.makedirs('data', exist_ok=True)
sys_df_dt[['reg', 'rand']].to_csv('data/sys_df_dt.csv') 
res_df_dt[['reg', 'rand']].to_csv('data/res_df_dt.csv') 


Суб'ект даных © Антон Парфенчык, 2023.

Працуе на Pelican і Twitter Bootstrap . Іконкі Font Awesome .