Блог Ромы Бунина о визуализации, Tableau и развитии BI-систем
канал в телеграмме | подборки примеров | подкасты и видео

Позднее Ctrl + ↑

Оптимизация работы отчетов

Статья подготовлена в рамках сериала «Залетай в BI»

После небольшого перерыва Руслан продолжает осваивать премудрости Табло. В этот раз мы сконцентрировались на производительности и разбирались с тем как записывать Performance Recording. Скорость работы отчетов — очень важный показатель, для счастья пользователей. Если вы научитесь делать быстрые дашборды, они будут вам очень благодарны. =)

В этот раз задача была сугубо техническая — разобраться, что влияет на скорость работы отчета, как это замерить и улучшать. Для этого Руслан проштормил кучу материалов и попробовал применить усвоенное к одному из дашбордов, что мы делали до этого. Был записан исходный перфоманс, сделаны гипотезы по улучшениям и проведены повторные замеры.

Исходный дашборд: https://public.tableau.com/app/profile/ruslan6180/viz/olist_new/Dashboard_City

Исходный перфоманс рекорд отчета:

Чтобы перфоманс рекорд был максимально идентичным все записи запускались через макрос-кликер, записывающий движение мышки. Также был отключен фильтр на длительность операции < 0.01 секунды, чтобы видеть все операции. Записи проводились локально

За точку старта берется событие с параметром tabdoc:navigate-to-sheet, а точкой окончания — последняя операция render, не относящаяся к окончанию работы перфоманс рекорда.

Гипотезы по улучшению отчета:

  1. Уменьшить кол-во выводимых строк в Sellers List (сейчас выводится 5к+ marks), используя пагинацию и оставить только топ 100 городов на карте (вместо 4к)
  2. Переделать график с боксплотами, оставив только диапазоны, без отдельных значений

Проверка гипотезы 1

Теперь в таблице sellers list выводится только 30 строк, а на карте только 100 значений. Результаты:

Проверка гипотезы 2

Переделаны боксплоты, чтобы не было выбросов. Результаты:

Итого

Финальный дашборд: https://public.tableau.com/app/profile/ruslan6180/viz/olist_new_performance/Dashboard_City
Результат в серднем для первичной загрузки -0,5 сек от исходного времени в 3,7 сек (или -13%). Дашборд и так был быстрый, но процентный прирост довольно значимый.

 Нет комментариев    11818   11 мес   табло

Порядок операций при работе с Relationships

Статья подготовлена в рамках сериала «Залетай в BI»

В прошлом спринте Руслан делал дашборд в котором столкнулся с интересным поведением Табло

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

Так как в одном заказе может быть несколько позиций, то для соединения этих таблиц логично использовать Relationships (далее рилейшены или макароны :-P).

Предположим, что мы хотим посчитать продажи по производителю«PA». Простой способ — добавить поле Producer в фильтры и выбрать нужного производителя:

Всё работает, как мы ожидаем и показывается верное число — 550 рублей. Но что, если мы хотели бы посчитать продажи не через фильтр, а через отдельное расчетное поле вида IIF([Producer]=«PA»,[Total Sales],NULL)?
Например, такое могло бы пригодиться, чтобы была возможность выбирать производителя через использование параметра и нам нужно было сравнивать параметр с значением поля Producer. В этом случае мы получаем ошибку задвоения по количеству товаров в заказе:

Чтобы раскопать эту ошибку в реальном дашборде у меня ушло больше часа. Наверное ещё мало работаю с этим типом соединений и не набил себе шишек. Но, к сожалению, не нашёл в интернете подробного описания как технически работают рилейшены.

Объясняю себе это так — в первом случае с использованием фильтров Табло сначала выбирает уникальные значения из правой таблицы, затем делает джоин с левой таблицей и только после этого делает расчет суммы для продаж. Во втором же случае мы не фильтруем правую таблицу, а используем её полностью, затем джоиним на левую таблицу и получаем задвоение, затем считаем сумму продаж.

Какой-то порядок операций в квадрате =) Было бы круто найти технический алгоритм работы этих макарон. Кажется, что можно наделать незаметных ошибок, которые приведут к неверному результату. Будьте осторожны!

Особенности работы функции TOTAL()

Статья подготовлена в рамках сериала «Залетай в BI»

На прошлой недели Руслан делал задачки на табличные функции и сделал ошибку при работе с функцией TOTAL(). Объясняю в чем она заключалась и как это работает.

Функций TOTAL() — уникальная функция в Табло, которая работает по своим правилам. Если их не знать, то легко сделать ошибку. Рассмотрим на примерах.

TOTAL() игнорирует дименшены в виде

Предположим, что у нас есть средние чеки по разным категориям товаров и мы хотим посчитать отклонение это среднего чека от среднего между этими категориями.

Сначала посчитаем средний чек между категориями. Для этого можем кинуть референс лайн со средним на вид:

Получили 445$ — это значение получается как (504+532+96+649)/4=445. А каждое слагаемое — это AVG(Sales) по всем строчкам данных внутри этой категории. То есть мы посчитали среднее от средних.

Теперь попробуем воспроизвести среднее от средних с помощью функции TOTAL():

Получим то, что значение считается по-другому, вместо 445, мы видим 350. Почему же так происходит? Функция TOTAL игнорирует все димешены в виде при расчёте и считает среднее на уровне строк базы данных. То есть мы по сути получили такой расчёт, как если бы в виде не было ни одного дименшена:

Игнорирование дименшенов — самое главное отличие функции TOTAL(), в отличии от других табличных вычислений она не зависит от разбивки на листе.

Если бы мы хотели посчитать средне от среднего нам бы было необходимо использовать функцию WINDOW_AVG(). Эта функция уже не игнорирует дименшены, а делает расчеты между агрегированными значениями, которые получились по каждому из дименшенов.

Поэтому если бы мы хотели построить график отклонения от среднего между категориями, а не от среднего в целом, то нам бы нужно было использовать именно эту функцию. Могли бы получить какую-то такую табличку:

Какой использовать из расчетов зависит от вашей задачи и, возможно, TOTAL() был бы даже логичнее использовать в данном кейсе. Но очень важно понимать как работает эта функция в данном случае.

Grand Total использует TOTAL()

Предположим, что мы хотим вывести таблицу в которой будут посчитаны количество уникальные товаров по городам (считаем, что название товара == его ID):

Теперь добавим итог для этой таблицы. Получим 110 товаров.

Пользователи, привыкшие к Экселю, подумают, что в итоге мы увидим сумму значений по строкам, но на самом деле это не так. Все итоги в Табло тоже используют функцию TOTAL() и так как мы считаем неаддитивную метрику, то он посчитает количество уникальных товаров среди всех городов. Чтобы переключить способ расчета на сумму, необходимо выбрать опцию Total using (таким образом на самом деле меняем расчет на WINDOW_SUM()). При это получим другой результат (114, а не 110)

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

Итого

Оба примера которые я привел в заметке очень часто встречаются в реальной жизни при расчетах DAU, LTV, Retention и других неаддитивных метрик. Будьте аккуратны и случайно не посчитайте что-то не то, что ожидает ваш пользователь.

LODы, порядок операций и как работает Табло

Статья подготовлена в рамках сериала «Залетай в BI»

На прошлой недели Руслан прокачивал навыки работы со сложным техническим функционалом. Для того чтобы прокачать работу с расчётами я дал задачки, где нужно было построить одни и те же графики при помощи табличных вычислений и LODов. Тут были две основные проблемы: переусложненные вычисления и ошибки в понимании как работает порядок операций. Разберём две эти проблемы на небольших примерах на любимом Sample Superstore.

Как работают LODы и Табло в целом

Предположим, что хотим посчитать для каждого штата процент продаж от общего с помощью LODа (понятно, что для этого лучше подошли бы тейбл кальки, это чисто учебная задача). Руслан сделал это с помощью такой формулы:

И в целом это работает верно, но это слишком сложно — мы используем целых два LODа вместо одного. Как же понять, что формула в числителе лишняя?

Во первых давай разберёмся какой SQL запрос послало Табло, когда мы просто собрали лист с суммой продаж по штатам. Когда мы кладём любые пилюли на полку Табло формирует запрос к базе данныых, получает в ответ агрегированную таблицу данных и визуализирует её. Если мы положили пилюли как на скрине, то Табло отправило примерно такой запрос (не ругайте меня за нотацию, у меня лапки на счёт SQL, сейчас важнее смысл). Это, кстати, называется VIZQL — проприетарная технология Табло превращения пилюль в запросы к БД.

Теперь давайте разберёмся как работает LOD (в данном случае FIXED). Level of Detail расчет, это такой расчёт, который создаёт отдельный подзапрос к базе данных и потом джойнит его к той агрегированной таблице, которая получилась в результате запроса от VIZQL листа. То есть этот запрос выполняется паралельно основному и потом происходит его джоин. Из-за этого LODы так медленно и работают на больших объёмах данных.

Структура выражения LOD при этом выглядит так:

Получается, что наша формула для расчета процента от общего превратиться в такие подзапросы:

Мы сразу видим, что первый подзапрос, по-сути идентичен тому запросу, что формирует сам лист без применения LOD. В этом случае получается, что мы переусложняем и вместо LOD мы можем использовать в числителе просто SUM([Sales]). Но при этом Табло будет ругаться на то, что мы смешиваем агрегированные и неагрегированные показатели:

Чтобы избавиться от этой ошибки нам нужно обернуть формулу в знаменателе в агрегацию. Например в сумму. Всё отлично работает.

Итого — проверяя свои расчёты, думайте о том, какие агрегаты вы уже получаете из данных, когда используете пилюли. Вам не нужно использовать LOD с такой же грануляций, а просто можно использовать агрегирующую функцию.

Порядок операций

Порядок операций — важный концепт, который как мне кажется часто переусложняют. В целом его обязательно нужно понимать, чтобы знать как происходит трансформация данных в Табло. Это спасёт вас от ошибок расчета TopN, сетов или поможет оптимизировать книгу, например, с помощью фильтров на стороне источника данных. Однако из практических кейсов когда мы можем управлять и менять порядок операций я выделю три основных:
— Превращение обычного расчета в FIXED, чтобы расчет «игнорировал» фильтры в визуализации
— Превращение обычного фильтра в контекстный, чтобы он начал действовать на FIXED расчёты и расчеты TopN
— Использование Table Calcs фильтров, чтобы отфильтровывать данные из вида, не убирая их из агрегированной таблицы.

Пока объяснял это Руслану, нарисовал такую схемку, вот прям живой скрин, поэтому немного страшненький:

Рассмотрим примеры для каждого из кейсов.

1. Превращение обычного расчета в FIXED
Здесь всё обычно супер очевидно, хотим подсчитать что-то, что будет «игнорировать» фильтры. Игнорировать в кавычках, так как на самом деле этот запрос выполняется параллельно. Бизнесовый пример — хотим при выборе конкретного сегмента товаров в фильтре видеть, какой процент продаж он составляет в регионе:

2. Превращение обычного фильтра в контекстный
Превращение фильтра в контекстный позволяет нам применить его «как бы на уровне датасорса». Это на самом деле создаёт временную таблицу к которой уже обращается VIZQL, что может как тормозить, так и иногда ускорять работу Табло. Если вы применяете контекстный фильтр ко всем листам в визуализации, подумайте на счёт его переноса в датасорс фильтры через параметры. Бизнесовый пример — хотим видеть топ-10 штатов по продажам за 2019 год. Используем для этого фильтр TopN, а чтобы он правильно работал при выборе года, фильтр по году заносим в контекст.

3. Использование Table Calcs фильтров
Чаще всего нужны чтобы работали верно расчеты прироста и функции LOOKUP. Так как если мы фильтруем с помощью дименшен фильтров, то данные отфильтровываются и Табло не почему строить приросты. Бизнесовый пример — хотим показывать текущий год на фоне предыдущего, но при этом хотим скрыть первый год.

Для этого можно использовать фильтр по FIRST()>-12. Так как это табличный фильтр он и идёт в самом конце фильтрации по порядку операций. Значит он не удалит данные из запроса, а только скроит их из вида, что нам и нужно.

Книга с этими примерами на Паблике.

Понимаю, что привёл примеры без детальных объяснений, но зато кратко и основные кейсы. Есил вам нужно прокачать эту часть поищите в инете по запросу Tableau Order of Operations. Вот, например, неплохое видео.

Шпаргалка по Quick Table Calculations

Статья подготовлена Русланом и немного подредактирована мной. Материал создан рамках менторства и прокачивания навыков Руслана как BI-разработчика. На нашем перовом созвоне я увидел, что у Руслана нет легкости в обращении с быстрыми табличными вычислениями, поэтому одно из заданий было разобраться с каждым видом расчета, чтобы прокачать этот навык. Он подготовил шпаргалку, которая будет полезна всем, кто только начал изучать Табло.

Задание

Придумать по одному бизнес кейсу для каждого Quick Table Calcs, которые есть в Табло. Описать какую задачу решает этот кальк, описать особенности этого калька (если есть).

Решение

Книга на Табло Паблик с примерами

Общая особенность формул: Для всех table calcs можно уточнять размер партиции (Таблица, панель, ячейка) и направление расчета, если партиция по структуре не ряд, а таблица. Направления — down, across и т. д.

Running total (Включает в себя быстрые YTD total, YTD growth)

Краткое описание

  • Агрегация нарастающим итогом.
  • YTD total. Суммы нарастающим итогом, разбитые по годам
  • YTD growth. Приросты к прошлому году нарастающим итогом, разбитые по годам

Формула
RUNNING_SUM(SUM([Sales]))

Особенности

  • Для агрегации можно использовать sum, avg, min, max
  • Расчет может быть основан на другой расчетной метрике (Secondary calculation). Т. е. можно сделать например нарастающий итог процента от общего.
  • Для того чтобы работали функции YTD и YTD Growth в виде обязательно должны быть год и месяц
  • Нет расчета RUNNING_COUNTD что очень не удобно для подсчета когорт уникальных пользователей

Кейсы

  • Все сценарии когда нам нужно смотреть на наши цифры нарастающим итогом. Например сравнивать факт и план, если план дается нарастающим итогом, а не разбит по периодам
  • Можем строить график парето. Понимать соотношение кол-ва элементов и их вклада (X% товарных позиций приносят Y% выручки)
  • Риал тайм цифра. Например количество открытых тикетов, которое складывается из всех открытых и закрытых тикетов за все время или количество проданных билетов за все дни

Difference from

Краткое описание
Разница текущего значения и выбранного (чаще всего предыдущего) в абсолютном выражении. Насколько приросли к выбранному периоду в абсолюте.

Формула
ZN(SUM([Sales])) — LOOKUP(ZN(SUM([Sales])), -1)

Особенности
Работает опция «Relative to» — выбранным значением может быть Previous, Next, First или Last

Кейсы

  • Можем использовать, чтобы сравнивать результаты текущего периода и прошлого
  • Рассчитывать разницу «отставания» между двумя категориями. Полезно для постройки waterfall чартов или гантельных диаграмм

Percent difference from и Year over Year growth rate

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

Формула
(ZN(SUM([Sales])) — LOOKUP(ZN(SUM([Sales])), -1)) / ABS(LOOKUP(ZN(SUM([Sales])), -1))
 
Особенности

  • Работает опция «Relative to» — выбранным значением может быть Previous, Next, First или Last
  • Расчет YoY доступен только если в виде есть год и месяц.

Кейсы

  • Year over Year growth rate. Сравнение результатов текущего периода и аналогичного периода год назад
  • График доходности актива. Если хотим смотреть % изменения относительно первого значения.

Percent from

Краткое описание.
Сравнение текущего значения и выбранного в процентном выражении. Какой % составляет текущее значение от выбранного.

Формула
ZN(SUM([Sales])) / LOOKUP(ZN(SUM([Sales])), -1)

Особенности
Выбранным значением может быть Previous, Next, First, Last
 
Кейсы

  • Расчет выполнения факта от плана в длинной структуре данных
  • Сравнение нескольких категорий в процентном соотношении

Percent of total

Краткое описание
Доля от общего.
 
Кейсы

  • Можем использовать чтобы понимать вклад отдельной категории продуктов в общие продажи
  • Строить нормированные бар-чарты и ареа-чарты

Rank

Краткое описание.
Порядковый для среза посчитанный по убыванию или возрастанию агрегата какой-то метрики

Формула
RANK(SUM([Sales]))

Особенности
Можно выбирать формулу выбора номера при совпадающих значения (Competition, dense, unique, modified competition)

Кейсы

  • Рейтинг с изменением по времени (бамп чарт)
  • Сортировать и фильтровать данные когда нельзя фильтр по TopN

Percentile

Краткое описание
Процентиль — определяет в какой процент от общего попадает значение для упорядоченного по метрике массива.

Формула
RANK_PERCENTILE(SUM([Sales]))

Кейс
Отобрать все записи, входящие в топ N% значений

Moving calculation

Краткое описание
Скользящая аггрегация. Вместо одного значения берет для расчета несколько, выбирая их относительно текущего

Формула
WINDOW_AVG(SUM([Sales]), -2, 0)

Особенности

  • Для агрегации можно использовать sum, avg, min, max
  • Выборка задается окном «от и после» текущего значения
  • Текущее значение и случаи когда не нет полных данных для окна можно исключать.

Кейсы
Если данные показывают большую вариабельность или имеют сезонность, то часто общая динамика непонятна. В этом случае используется скользящее среднее.

Дополнительные материалы

Видео от Энди Крибела
Материалы Табло Марафона
Официальная документация

Последовательный выбор двух параметров одним экшеном

Редко пишу такие технические статьи про Табло, но недавно придумал довольно интересное решение, захотелось им поделиться.

Идея: при кликах в один и тот же график менять последовательно два параметра — первый клик меняем один параметр, второй клик — меняем второй параметр. Например, можно использовать для выбора метрик для двух осей скеттер плота или придумать какие-то другие более необычные кейсы.

Давайте рассмотрим как это сделать. Эта инструкция подойдёт только для данных в широком формате, при подключении статичных файлов перевернуть таблицу можно при помощи встроенной функции Pivot.

1) Делаем счётчик кликов в график. Для этого создаём INT параметр Counter и поле Counter + 1, которое будет прибавлять количество кликов

2) Создаем два одинаковых параметра для выбора метрика. Это текстовые параметры, которые берут значения из поля Pivot Fields Names при открытии книги.

3) Создаём поля, которые проверяют какой сейчас номер клика в визуализацию и возвращают либо то же самое значение, что уже было выбрано, либо выбирают новое.

Selected Metric 1 Check // Название поля
IIF([Counter]%2=0,[Pivot Field Names],[Select Metric 1]) //Делает проверку остатка от деления на 2, если равно нулю, то клик «четный» и надо поменять значение, если «нечетный» то возвращаем текущее значение параметра

Для выбора второй метрики будет зеркальная формула:

Selected Metric 2 Check 
IIF([Counter]%2!=0,[Pivot Field Names],[Select Metric 2])

С помощью остатка от деления можно считать и третий, и четвертый клик и т. п.

4) Делаем расчетные поля, которые подставляют нужную метрику в зависимости от параметра. Эти поля мы и будем выводить на график.

Selected Metric 1 
IIF([Select Metric 1]=[Pivot Field Names],[Pivot Field Values],NULL)

Selected Metric 2
IIF([Select Metric 2]=[Pivot Field Names],[Pivot Field Values],NULL)

5) Создаём график с которого будет происходит управление. Я сделал для этого фактоиды. Важно положить все расчетные поля в details, чтобы мы могли использовать их в Parameters Actions.

6) Создаём график в котором будут выбираться метрики

7) Делаем дашборд и настраиваем три параметр экшена. Первый про изменение счётчика при клике.

Два вторых про изменение выбранной метрики

8) Наводим марафет и добавляем всякий приятности типа подсказки, деселекта хайлата и т. п. Получаем рабочую версию. Можете покликать и посмотреть как будет меняться значение Counter и выбранных метрик для осей. На Паблике можно скачать книгу и покопаться как устроено.

___

Итоги
Рассмотренный кейс довольно специфичный и, наверное, не является лучшей практикой UX паттернов, но с помощью него хотел показать, что возможности Табло по сложным интерфейсам — очень большие. Используя параметр экшены и счётчики можно сделать почти всё что угодно. Например, выводить какую-то надпись, если кликнули больше 100 раз. =)

Использовал приём с двумя параметрами для вот этой работы, получилось довольно удобно, но работает не супер быстро, много точек на графиках.

Ранее Ctrl + ↓