#!/usr/bin/env python
# coding: utf-8
# ## Присоединяйтесь к чату воркшопа:
# # https://clck.ru/9yRpd
# ## Подходите за материалами (на флешках)
# # Один день из жизни нагрузочного тестировщика
# ## Алексей Лавренюк, Яндекс
# Процесс нагрузочного тестирования:
#
# * Анализ работы сервиса:
# * архитектура
# * нагрузка (планируемая или существующая)
# * Подготовка стрельб
# * настройка тестового стенда
# * подготовка патронов
# * [Стрельбы](http://www.failoverconf.ru/upload/iblock/8ce/2_01_YandexTank.pdf)
# * Анализ результатов
#
# Из вышеперечисленного сегодня мы займемся анализом входной нагрузки и анализом результатов стрельб.
# ## Задачка 0: готовим тестовое окружение
#
# Логи, с которыми мы будем работать -- на флешках:
#
# * `access_log_Jul95` -- логи веб-сервера NASA Kennedy Space Center за июль 1995 года
# * `phout_1` и `phout_2` -- логи стрельб Яндекс.Танком
#
# Страничка с описанием логов NASA: http://ita.ee.lbl.gov/html/contrib/NASA-HTTP.html
#
# Если будете использовать jupyter, запустите его в той папке, куда скачали логи.
#
# Проверьте, что библиотеки имортируются:
# ```
# import numpy as np # numpy
# import pandas as pd # pandas
#
# # альтернативная библиотека для визуализации
# # https://github.com/mwaskom/seaborn
# import seaborn as sns
#
# # подключаем графику прямо в браузер
# %matplotlib inline
# ```
# Если нет -- самое время их поставить.
# ```
# sudo -H pip3 install numpy pandas seaborn
# ```
# Если вы под ubuntu, вероятно, для сборки этих библиотек вам понадобятся:
# ```
# sudo apt install build-essential python-dev gfortran
# ```
# # Парсим access.log
# In[ ]:
# немного о jupyter. vim-like, помощь, автодополнение
# In[ ]:
# In[3]:
import numpy as np
get_ipython().run_line_magic('pinfo', 'np.linspace')
# In[4]:
get_ipython().run_cell_magic('bash', '', '# можно исполнять bash-команды прямо отсюда\n\nhead ./access_log_Jul95\n')
# In[5]:
# мы на PyCon, поэтому будем работать с помощью преимущественно в Python
# читаем первую строку:
with open("./access_log_Jul95") as datafile:
log_line = datafile.readline()
print(log_line)
# ## Задачка 1: парсим access.log
#
# **Задание**
# Написать функцию `parse_line`, которая принимает на вход строку из лога и возвращает массив полей:
# * hostname - имя хоста, например `199.72.81.55`
# * date - дата в виде строки, например `01/Jul/1995:00:00:01 -0400`
# * method - метод, указанный в запросе, например `GET`
# * url - url, указанный в запросе, например `/history/apollo/`
# * response_code - код ответа, например `200`
# * size - размер ответа, например `6245`
#
# **вспомогательные материалы**
# * функции [split](https://docs.python.org/3.5/library/stdtypes.html#str.split), [rsplit](https://docs.python.org/3.5/library/stdtypes.html#str.rsplit) и [strip](https://docs.python.org/3.5/library/stdtypes.html#str.strip)
# * документация по [regex](https://docs.python.org/3/library/re.html) в питоне (а именно [re.match](https://docs.python.org/3/library/re.html#re.match))
# * [сервис](http://rubular.com) для тестирования регэкспов
#
# **заготовка кода**
# ```
# with open("./access_log_Jul95") as datafile:
# log_line = datafile.readline()
#
# def parse_line(line):
# # ваш код тут
# return []
#
# print(parse_line(log_line))
# ```
# In[29]:
# andrey-yantsen
def parse_line_t(line):
ip, _, _, dt, tz, method, path, _, responce_code, size = line.split(' ')
return ip, dt + tz, method.replace('"', ''), path, responce_code, size
def parse_line_t(line):
ip, _, _, dt, tz, method, path, _, responce_code, size = line.split(' ')
return ip, (dt + tz).replace('[', '').replace(']', ''), method.replace('"', ''), path, responce_code, size.strip()
print(parse_line_t(log_line))
#
#
#
# In[8]:
# вариант с регэксом
import re
# регэкс скомпилим заранее
regex = re.compile('([\w\-\.]+) - - \[(.*?)\] "([A-Z]+) (\S+)\s+(\S+)" (\d+) (\d+)')
def parse_line_re(line):
return regex.match(line).groups()
parse_line_re(log_line)
# In[9]:
# вариант без регэкспа: постепенно откусываем поля по известным разделителям
def parse_line_split(line):
host, line = line.split(" ", 1)
_, line = line.split("[", 1)
date, line = line.split("]", 1)
_, line = line.split('"', 1)
req, line = line.split('"', 1)
code, timing = line.strip("\r\n ").split()
return [host, date] + req.split(" ") + [code, timing]
parse_line_split(log_line)
# In[31]:
get_ipython().run_line_magic('timeit', 'parse_line_re(log_line)')
get_ipython().run_line_magic('timeit', 'parse_line_split(log_line)')
get_ipython().run_line_magic('timeit', 'parse_line_t(log_line)')
# ## Тестируем на нашем логе
# In[16]:
def test_parser_on_file(parser):
with open("./access_log_Jul95") as datafile:
for line in datafile:
try:
parser(line)
except Exception as e:
# если парсер не сработает, напечатаем строку,
# на которой он сломался, и выйдем
print(line)
raise e
# In[17]:
# сначала попробуем на функции, которая ничего не делает
# python 2 работает по-другому =)
test_parser_on_file(lambda l: l)
#
#
#
# In[18]:
get_ipython().run_cell_magic('bash', '', "# посмотрим, что же это за 0x80\n\nhexdump ./access_log_Jul95 | grep -m 1 ' 80 '\n")
# In[19]:
get_ipython().run_cell_magic('bash', '', "\nxxd ./access_log_Jul95 | grep '0c86e60' -C 2 -m 1\n")
# In[20]:
# пофиксим читалку файла
def test_parser_on_file(parser):
with open("./access_log_Jul95", encoding="ascii", errors="replace") as datafile:
for line in datafile:
try:
parser(line)
except Exception as e:
# если парсер не сработает, напечатаем строку,
# на которой он сломался, и выйдем
print(line)
raise e
test_parser_on_file(lambda l: l)
# In[21]:
# протестируем вариант с регэкспом
test_parser_on_file(parse_line_re)
# In[22]:
test_parser_on_file(parse_line_t)
#
#
#
# In[23]:
# если сервер возвращает ошибку, то размера нет
# добавим поддержку для отсутствующего размера
regex = re.compile('([\w\-\.]+) - - \[(.*?)\] "([A-Z]+) (\S+)\s+(\S+)" (\d+) (-|\d+)')
def parse_line_re(line):
return regex.match(line).groups()
# и проверим, что все работает на этой строке
parse_line_re('dd15-062.compuserve.com - - [01/Jul/1995:00:01:12 -0400] "GET /news/sci.space.shuttle/archive/sci-space-shuttle-22-apr-1995-40.txt HTTP/1.0" 404 -')
# кстати, если бы мы парсили не регэкспом, а вручную, мы могли бы в случае отсутствия значения поставить там
# 0 или None, и потом нам было бы удобнее работать с такими данными
# In[24]:
# попробуем еще раз
test_parser_on_file(parse_line_re)
# In[25]:
# учитываем возможное отсутствие протокола в запросе
regex = re.compile('([\w\-\.]+) - - \[(.*?)\] "([A-Z]+) (\S+)(?: (\S+))?" (\d+) (-|\d+)')
def parse_line_re(line):
return regex.match(line).groups()
parse_line_re('pipe6.nyc.pipeline.com - - [01/Jul/1995:00:22:43 -0400] "GET /shuttle/missions/sts-71/movies/sts-71-mir-dock.mpg" 200 946425')
# In[27]:
# посмотрим все необработанные строки (опасно, но я пробовал)
regex = re.compile('([\w\-\.@]+) - - \[(.*?)\] "([A-Z]+) (\S+)(?:\s+(\S+))?" (\d+) (-|\d+)')
def parse_line_re(line):
try:
return regex.match(line).groups()
except AttributeError:
print(line)
# не будем бросать тут эксепшн, просто продолжим
raise
return None
test_parser_on_file(parse_line_re)
# ## Задачка 2: починка парсера
#
# **Задание** Починить парсер хотя бы для тех ответов, которые обработал сервер
# * нераспаршенными должны остаться только те ответы, на которые сервер выдал код 400 (bad request)
# * предположим, что сервер читает url до первого пробельного символа, все остальное выкидывает
#
# **Заготовка кода**
#
# Можно либо поменять регулярное выражение, либо использовать свою функцию.
# ```
# import re
#
# def test_parser_on_file(parser):
# with open("./access_log_Jul95", encoding="ascii", errors="replace") as datafile:
# for line in datafile:
# try:
# parser(line)
# except Exception as e:
# # если парсер не сработает, напечатаем строку,
# # на которой он сломался, и выйдем
# print(line)
# raise e
#
# regex = re.compile('([\w\-\.@]+) - - \[(.*?)\] "([A-Z]+) (\S+)(?:\s+(\S+))?" (\d+) (-|\d+)')
# def parse_line_re(line):
# try:
# return regex.match(line).groups()
# except AttributeError:
# print(line)
# return None
#
# test_parser_on_file(parse_line_re)
# ```
# In[34]:
#briskly
import re
def parse_line(line):
res = re.match(r"(.*)- - (\[.*\]) \"(\w+) (.+)( (.+))?\" (\d+) (\d+|-)", line)
if res is None:
print(line)
return
return res.groups()
def parser_log(fn):
for line in open(fn, encoding="ascii", errors="replace"):
res = parse_line(line)
if res:
pass
parser_log("access_log_Jul95")
# In[28]:
# beloborodov
def parse_line(line):
return re.search('(.*) - - \[(.*)\] "(.*) (\/.*) HTTP/1.0" (.*) (.*)', line).groups()
test_parser_on_file(parse_line)
#
#
#
# **что получилось у меня**
# * запросом считаем то, что после метода и до первого пробельного символа, далее выкидываем все до последней кавычки
# * учитываем то, что после метода перед урлом может быть не один пробел
# * добавил все символы, которые встречались в хостнеймах. Можно было просто ограничить хостнейм первым пробельным символом, но мне было интересно, какие бывают хостнеймы
# In[35]:
regex = re.compile('([\w\-\.@\'*,:/#&]+) - - \[(.*?)\] "([A-Z]+)\s+(\S+).*" (\d+) (-|\d+)')
def parse_line_re(line):
try:
return regex.match(line).groups()
except AttributeError:
print(line)
# не кидаем эксепшн, посмотрим все нераспаршенные строки
return None
test_parser_on_file(parse_line_re)
# # Считаем урлы
# In[36]:
# убираем печать ненайденных урлов, чтобы не мешались
regex = re.compile('([\w\-\.@\'*,:/#&]+) - - \[(.*?)\] "([A-Z]+)\s+(\S+).*" (\d+) (-|\d+)')
def parse_line_re(line):
try:
return regex.match(line).groups()
except AttributeError:
return None
# In[37]:
from collections import defaultdict
# нас интересуют урлы без параметров, параметры могут быть и через хеш, и через вопрос
# отделим их с помощью регэкспа
delim = re.compile("[#?]")
counts = defaultdict(int)
with open("./access_log_Jul95", encoding="ascii", errors="replace") as datafile:
for line in datafile:
fields = parse_line_re(line)
if fields:
uri = delim.split(fields[3])[0]
counts[uri] += 1
counts
# In[38]:
import json
with open("urls.json", "w") as of:
json.dump(counts, of, indent=2)
# ## Задачка 3: топ 10 урлов
#
# **Задание** Найти 10 самых посещаемых урлов и число посещений для каждого из них
#
# **Заготовка кода**
# ```
# import re
# from collections import defaultdict
#
# regex = re.compile('([\w\-\.@\'*,:/#&]+) - - \[(.*?)\] "([A-Z]+)\s+(\S+).*" (\d+) (-|\d+)')
# def parse_line_re(line):
# try:
# return regex.match(line).groups()
# except AttributeError:
# return None
#
# # нас интересуют урлы без параметров, параметры могут быть и через хеш, и через вопрос
# # отделим их с помощью регэкспа
# delim = re.compile("[#?]")
#
# def count_urls():
# counts = defaultdict(int)
# with open("./access_log_Jul95", encoding="ascii", errors="replace") as datafile:
# for line in datafile:
# fields = parse_line_re(line)
# if fields:
# uri = delim.split(fields[3])[0]
# counts[uri] += 1
# return counts
#
# def top_ten():
# # ваш код здесь
# return []
# ```
# In[41]:
#andrey-yantsen
import json
urls = json.loads(open('urls.json').read())
list(sorted(urls.items(), key=lambda x: x[1], reverse=True))[:10]
#
#
#
# In[42]:
# не оптимально, но нас устроит
list(reversed(sorted(counts.items(), key=lambda x: x[1])))[:10]
# # Подключаем Pandas
# In[43]:
import numpy as np # numpy
import pandas as pd # pandas
# альтернативная библиотека для визуализации
# https://github.com/mwaskom/seaborn
import seaborn as sns
# подключаем графику прямо в браузер
get_ipython().run_line_magic('matplotlib', 'inline')
# In[44]:
# основное понятие в Pandas -- DataFrame
df = pd.DataFrame([
["Stuart Bloom", False, 50],
["Sheldon Cooper", True, 2000],
["Amy Farrah Fowler", False, 1200],
["Radjesh Koothrappali", False, 1000],
["Howard Wolowitz", False, 1500],
["Bernadette Rostenkowski", True, 2100],
["Penny", True, 5000],
["Leonard Hofstadter", True, 2500],
], columns="name pythonista salary".split())
df
# In[45]:
# селектим только питонистов
df[df['pythonista']]
# In[46]:
# как это работает? Индексация по массиву значений.
df['pythonista']
# In[47]:
df[[False, False, True, False, False, False, False, False]]
# In[48]:
# выберем только интересные нам колонки
df[['pythonista', 'salary']]
# In[49]:
print(type(df['pythonista']))
df['pythonista']
# In[51]:
# можно и так, но надо быть аккуратнее. Если колонка называется, например, size,
# то не сработает -- потому что есть функция с таким же названием
print(type(df.pythonista))
df.pythonista
# In[50]:
# выбираем одну колонку как датафрейм
print(type(df[['pythonista']]))
df[['pythonista']]
# In[52]:
# добавим новую колонку, приготовив ее из старой
df['first_name'] = df['name'].apply(lambda x: x.split()[0])
df
# ## Задачка 4: Добавить фамилию
#
# **Задание** Добавить колонку с фамилией. Если фамилия двойная, в колонке должны быть обе ее части. Если фамилии нет, то в колонке должно быть None
#
# **Заготовка кода**
# ```
# df = pd.DataFrame([
# ["Stuart Bloom", False, 50],
# ["Sheldon Cooper", True, 2000],
# ["Amy Farrah Fowler", False, 1200],
# ["Radjesh Koothrappali", False, 1000],
# ["Howard Wolowitz", False, 1500],
# ["Bernadette Rostenkowski", True, 2100],
# ["Penny", True, 5000],
# ["Leonard Hofstadter", True, 2500],
# ], columns="name pythonista salary".split())
#
# # тут ваш код
# ```
# In[54]:
# andrey-yantsen
df['name'].apply(lambda x: '' if ' ' not in x else x.split(' ')[1])
# In[55]:
def ln(i):
names = i.split()
try:
return names[1]
except IndexError:
return names[0]
df['name'].apply(ln)
# In[56]:
#khudyakovavi
df['name'].apply(lambda x: None if ' ' not in x else ' '.join(x.split()[1:]))
#
#
#
# In[57]:
def split_surname(name):
parts = name.split(" ", 1)
if len(parts) > 1:
return parts[1]
else:
return None
df['surname'] = df['name'].apply(split_surname)
df
# In[60]:
# средняя зарплата питонистов и не питонистов
df.groupby('pythonista').mean()
# In[61]:
# Стюарт и Пенни портят всю картину =) Попробуем медиану
df.groupby('pythonista').median()
# In[62]:
# не доверяйте средним значениям за весь тест. Вы можете упустить выбросы, например, пики из за GC.
sns.plt.scatter(df['pythonista'], df['salary'])
# ## Загрузим лог в Pandas
#
# **вспомогательные материалы**: [Генераторы](https://wiki.python.org/moin/Generators) в python
#
# **код**:
#
# ```
# regex = re.compile('([\w\-\.@\'*,:/#&]+) - - \[(.*?)\] "([A-Z]+)\s+(\S+).*" (\d+) (-|\d+)')
# delim = re.compile("[#?]")
#
# def parse_log_re(file):
# for line in file:
# try:
# # заодно отделим параметры от урла
# fields = regex.match(line).groups()
# uri_parts = delim.split(fields[3], 1)
# uri = uri_parts[0]
# if len(uri_parts) > 1:
# params = uri_parts[1]
# else:
# params = None
# yield list(fields) + [uri, params]
# except AttributeError:
# pass
#
# columns = "host ts method uri code size url params".split()
# with open("./access_log_Jul95", encoding="ascii", errors="replace") as datafile:
# df = pd.DataFrame.from_records(parse_log_re(datafile), columns=columns)
# ```
# In[63]:
regex = re.compile('([\w\-\.@\'*,:/#&]+) - - \[(.*?)\] "([A-Z]+)\s+(\S+).*" (\d+) (-|\d+)')
delim = re.compile("[#?]")
def parse_log_re(file):
for line in file:
try:
# заодно отделим параметры от урла
fields = regex.match(line).groups()
uri_parts = delim.split(fields[3], 1)
uri = uri_parts[0]
if len(uri_parts) > 1:
params = uri_parts[1]
else:
params = None
yield list(fields) + [uri, params]
except AttributeError:
pass
columns = "host ts method uri code size url params".split()
with open("./access_log_Jul95", encoding="ascii", errors="replace") as datafile:
df = pd.DataFrame.from_records(parse_log_re(datafile), columns=columns)
# In[64]:
df.head()
# In[65]:
df.dtypes
# In[67]:
df["size"] = pd.to_numeric(df["size"], errors='coerce')
df["ts"] = pd.to_datetime(df["ts"], format="%d/%b/%Y:%H:%M:%S -0400")
# In[68]:
df.dtypes
# In[69]:
df.head()
# In[70]:
# булевы операции в numpy
a = np.array([True, True, False, False])
b = np.array([True, False, True, False])
print("~a:", ~a)
print("a & b:", a & b)
print("a | b:", a | b)
# In[71]:
# посмотрим, какие бывают параметры в урлах
df[~df["params"].isnull()]
# In[75]:
# построим распределение размеров ответов
# создаем оси, чтобы можно было задать размер
fig, ax = sns.plt.subplots()
# и задаем размер
fig.set_size_inches(16,10)
# в параметрах указываем оси
df[["size"]].hist(bins=20, ax=ax)
# In[76]:
# логарифмический масштаб по оси y
fig, ax = sns.plt.subplots()
fig.set_size_inches(16,10)
df[["size"]].hist(bins=20, ax=ax)
# задаем логарифмический размер
ax.set_yscale("log")
# ## Минизадачка 5: Посмотреть, что за пик
#
# **Задание** Посмотреть, какие запросы относятся к пику, который мы видим на графике.
#
#
#
# In[80]:
#butorov
df[df['size'] > 2000000]
# In[77]:
df[df["size"] > 2500000]
# In[79]:
# чтобы сделать логарифмический масштаб по оси x, нам нужны логарифмические бины
# сгенерим их с помощью numpy.logspace
fig, ax = sns.plt.subplots()
fig.set_size_inches(16,10)
# обратите внимание на параметр bins
df[["size"]].hist(bins=np.logspace(0.1, 10.0, 20), ax=ax)
#df[["size"]].hist(bins=20, ax=ax)
# логарифмический масштаб оси по x
ax.set_xscale("log")
# ## Задачка 6: считаем RPS на сервере
#
# **Задание**: найти число запросов в секунду для каждой секунды, когда они были
# In[81]:
# khudyakovavi
df.groupby('ts').size()
#
#
#
# In[82]:
rps = df.groupby("ts")[["method"]].count()
rps.head()
# In[83]:
rps.max()
# In[84]:
# построим график RPS
fig, ax = sns.plt.subplots()
fig.set_size_inches(16,10)
rps.plot(ax=ax)
# In[85]:
# добавим скользящее среднее, чтобы сгладить пики
fig, ax = sns.plt.subplots()
fig.set_size_inches(16,10)
#rps.plot(ax=ax)
# rolling сделает нам группы, используя скользящее окно, по которым можно считать разное
roll = rps.rolling(window=600)
roll.mean().plot(ax=ax, c="red")
# ## Творческая задачка 7: исследовать пик
#
# **Задание**: узнать, с чем связан пик на графике
# In[88]:
#vanadium23
from datetime import datetime
df[df['ts'] > datetime(1995, 7, 13)][df['ts'] < datetime(1995, 7, 15)].groupby('uri').count()
#
#
#
# In[89]:
# для начала сравним на одном графике обычный и необычный дни
# возьмем время без даты
df['time'] = df["ts"].dt.time
# и отсеим нужные нам данные
event = df[
(df['ts'] > pd.Timestamp("1995-07-13 00:00:00")) &
(df['ts'] < pd.Timestamp("1995-07-14 00:00:00"))
]
no_event = df[
(df['ts'] > pd.Timestamp("1995-07-14 00:00:00")) &
(df['ts'] < pd.Timestamp("1995-07-15 00:00:00"))
]
# оси
fig, ax = sns.plt.subplots()
fig.set_size_inches(16,10)
# скользящее среднее для дня с событием
event_rps = event.groupby("time")[["method"]].count()
event_roll = event_rps.rolling(window=600)
event_roll.mean().plot(ax=ax, c="red")
# скользящее среднее для дня без события
no_event_rps = no_event.groupby("time")[["method"]].count()
no_event_roll = no_event_rps.rolling(window=600)
no_event_roll.mean().plot(ax=ax, c="green")
# In[90]:
# урлы, на которые ходили в четверг, но не ходили в пятницу
set(event['url']) - set(no_event['url'])
# In[91]:
# посмотрим на разницу в топ100
visits_event = event.groupby('url')[['method']].count().sort_values(by='method', ascending=False)
visits_no_event = no_event.groupby('url')[['method']].count().sort_values(by='method', ascending=False)
set(visits_event[:100].index) - set(visits_no_event[:100].index)
# https://en.wikipedia.org/wiki/STS-70
# # Анализируем результаты стрельб Танком
# In[92]:
# функция для импорта сырого отчета phantom
phout_columns = [
'time', 'tag', 'interval_real',
'connect_time', 'send_time',
'latency', 'receive_time',
'interval_event', 'size_out',
'size_in', 'net_code', 'proto_code']
def read_phout(filename):
data = pd.read_csv(
filename, sep='\t', names=phout_columns)
# хитрые манипуляции с колонками
# в логе есть время отправки и время выполнения запроса. Суммируем их и получается
# время получения ответа - будем группировать по нему (заводим новую колонку)
data['ts'] = data.time + data.interval_real / 1000000
# округляем до секунды
data['receive_sec'] = data.ts.astype(int)
# и индексируем по этой секунде
data.set_index(['receive_sec'], inplace=True)
# для удобства посчитаем время ответа в миллисекундах
data['rt_ms'] = data.interval_real / 1000
return data
# In[93]:
phout1 = read_phout("phout_1.log")
phout2 = read_phout("phout_2.log")
# In[94]:
# графики RPS
fig, ax = sns.plt.subplots()
fig.set_size_inches(16,10)
phout1.groupby(level=0).count().time.rolling(window=10).mean().plot(title="RPS 1", ax=ax)
fig, ax = sns.plt.subplots()
fig.set_size_inches(16,10)
phout2.groupby(level=0).count().time.rolling(window=10).mean().plot(title="RPS 2", ax=ax)
# In[95]:
# группируем по индексу, считаем медиану, среднее и максимум и рисуем график
fig, ax = sns.plt.subplots()
fig.set_size_inches(16,10)
phout1.groupby(level=0).rt_ms.agg([np.mean, np.median, np.max]).plot(ax=ax)
# In[96]:
# медиана и среднее
fig, ax = sns.plt.subplots()
fig.set_size_inches(16,10)
phout1 = phout1[2000:-1000]
phout1.groupby(level=0).rt_ms.agg([np.mean, np.median]).plot(ax=ax)
# In[97]:
# сглаженные медиана и среднее
fig, ax = sns.plt.subplots()
fig.set_size_inches(16,10)
phout1 = phout1[2000:-1000]
phout1.groupby(level=0).rt_ms.agg([np.mean, np.median]).rolling(window=10).mean().plot(ax=ax)
# In[98]:
# сглаженные медиана, среднее и rps на одном графике для второго лога
fig, ax = sns.plt.subplots()
fig.set_size_inches(16,10)
phout2.groupby(level=0).count().time.rolling(window=10).mean().plot(title="RPS vs timings", ax=ax)
phout2.groupby(level=0).rt_ms.agg([np.mean, np.median]).rolling(window=10).mean().plot(ax=ax, secondary_y=True)
# In[99]:
# сглаженные медиана, среднее и rps на одном графике для времен соединения, отправки, обработки и получения данных
fig, ax = sns.plt.subplots()
fig.set_size_inches(16,10)
phout2.groupby(level=0).count().time.rolling(window=10).mean().plot(title="RPS vs subtimings mean", ax=ax)
(phout2[[
"interval_real", 'connect_time', 'send_time',
'latency', 'receive_time']] / 1000.0).groupby(level=0).agg(np.mean).rolling(window=10).mean().plot(ax=ax, secondary_y=True)
# In[100]:
# процентильный график
def percentile(n):
def percentile_(x):
return np.percentile(x, n)
percentile_.__name__ = 'percentile_%s' % n
return percentile_
percentiles = [percentile(n) for n in [1, .75, .5, .25, 0]]
fig, ax = sns.plt.subplots()
fig.set_size_inches(16,10)
phout2.groupby(level=0).count().time.rolling(window=10).mean().plot(title="RPS vs timings", ax=ax)
phout2.groupby(level=0).rt_ms.agg(percentiles).rolling(window=10).mean().plot(
title='Phout2 percentiles vs rps', kind='area', stacked=False, figsize=(12, 10), linewidth=0, ax=ax, secondary_y=True)