7.37K subscribers
1.73K photos
73 videos
1 file
1.3K links
Привет! Мы — образовательная платформа в сфере аналитики Simulative: simulative.ru

Создаём курсы-симуляторы, где обучаем не на «апельсинках», а на кейсах из реального бизнеса.

Наш уютный чат: @itresume_chat
Поддержка: @simulative_support
Download Telegram
🔥 Бесплатный live-курс по SQL для начинающих 🔥

Приветствую!

На связи Андрон, CEO Simulative & IT Resume.

Хочу пригласить вас на свой бесплатный live-курс по SQL для начинающих: "Сегментация базы пользователей", который пройдёт с 16 по 17 мая в 19:00 по мск. 💥

Этот live-курс подойдет Вам, если:

🔹 У вас совсем нет опыта в SQL;
🔹 Вы немного владеете базовым SQL, но нет системности;
🔹 Вы никогда не применяли SQL для решения бизнес-задач.

На live-курсе помимо лекций будут 2 блока домашних заданий, доступ к боевому кластеру и реальный бизнес-кейс, который вам предстоит решить самостоятельно.

КУРС АБСОЛЮТНО БЕСПЛАТНЫЙ! 😉

🔗 Чтобы не пропустить трансляцию и получить конспект, переходите по ссылке 👉 https://mnlp.cc/mini?domain=simulative&id=7.

До встречи! 🙂
🔥8👍3🤩2
💥 Напоминание о бесплатном live-курсе по SQL 💥

Приветствуем!

Возможно, вы пропустили наш прошлый пост, поэтому дублируем информацию еще раз 🙃

Приглашаем вас на бесплатный live-курс по SQL для начинающих: "Сегментация базы пользователей", который пройдёт с 16 по 17 мая в 19:00 по мск. 🔥

Что вас ждет на live-курсе:

🔸 2 живых обучающих лекции с разбором практических заданий;
🔸 2 блока домашних задания (15 заданий);
🔸 Самостоятельное решение бизнес-кейса с обратной связью от преподавателя;
🔸 Доступ к боевому кластеру;
🔸 Работа с профессиональными инструментами и программами.

🔗 Чтобы не пропустить трансляцию и получить конспект, переходите по ссылке 👉 https://mnlp.cc/mini?domain=simulative&id=7.

До встречи на live-курсе! 😉
🔥9👍2
⁉️ А есть ли разница между Concat / Сoncat_ws / ||

Как часто вас учат, что можно заменить функцию concat оператором ||? А что, если мы скажем, что это не совсем так? Мы вместе с нашими студентами курса разбирались в этом во время решения домашних задач.

✏️ В PostgreSQL оператор || - это бинарный оператор, который используется для конкатенации двух строковых значений. Он просто соединяет две строки вместе, не добавляя никаких разделителей или других символов между ними.

SELECT 'IT' || 'Resume';
-- ITResume

В то время как функции concat() и concat_ws() также используются для конкатенации, но предоставляют некоторые дополнительные возможности. Например, функция concat() позволяет объединять любое количество строковых аргументов, а concat_ws() еще и принимает первым аргументом разделитель.

SELECT concat('IT', ' ', 'Resume');
-- IT Resume

SELECT concat_ws('-', '2023', '05', '15');
-- 2023-05-15

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

А что с NULL значениями?

Вот и ловушка! При использовании оператора || в PostgreSQL, если один из аргументов NULL, результат будет также NULL. Например:

SELECT 'SQL' || NULL;
-- NULL

А вот функции, вполне справятся с NULL значениями:

SELECT concat(NULL, NULL);
-- ''

SELECT concat_ws(' ', 'IT', NULL, 'Resume');
-- IT Resume

Почему так?

Оператор || работает по стандарту SQL, когда любое выражение, в котором участвует NULL должно равняться NULL. Потому что NULL это неизвестность. Как 42 * NULL даст NULL.

А функции concat и concat_ws если какой-то из элементов NULL - проигнорируют его и соединят остальное, но если все NULL - вернут пустую строку.

❗️ Так что будьте осторожны и присоединяйтесь к Симулятору «Аналитик данных», чтобы оценить свои навыки на рабочих кейсах!

#sql
🔥15👍5
📢 [Ласт-колл] на бесплатный live-курс по SQL для начинающих

Это Андрон, основатель IT Resume & Simulative. Просто хочу напомнить, что сегодня в 19:00 по мск пройдет первый день бесплатного live-курса по SQL для начинающих: "Сегментация базы пользователей"

🔗 Чтобы не пропустить трансляцию и получить конспект, переходите по ссылке 👉 https://mnlp.cc/mini?domain=simulative&id=7.

До встречи на трансляции! 😉
🔥42👍1
🤨 Действительно ли параметр inplace в Pandas ускоряет операции?

Многие пользователи Pandas считают, что использование операций с параметром inplace=True ускорит работу с датафреймами. Согласны ли вы с этим? Всегда ли это работает?

🟢 Давайте это проверим

Inplace - это параметр методов Pandas, который позволяет изменять DataFrame без создания нового объекта. Звучит так, будто это должно работать быстрее, но есть некоторые нюансы:

- Inplace, вопреки тому, что следует из названия, часто не препятствует созданию копий. Это первый пункт против скорости подобного способа.
- SettingWithCopy - часто получаемое предупреждение - оно не влияет на выполнение кода. Однако же получается Pandas выполняет дополнительные проверки, чтобы убедиться, что датафрейм изменен корректно. Соответственно, второй пункт против скорости inplace.
- И последнее - inplace операции могут быть менее гибкими, так как не позволяют объединить несколько методов в одной строке.

Но это лишь предположения, давайте убедимся в них!

🔵 Мы провели небольшой анализ

Смотрите в карточке под постом как некоторые методы работают с inplace.

❗️Какие-то методы действительно работают быстрее, некоторые гораздо быстрее, но все-таки большинство - медленнее.

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

🧠 Если хотите все проверить самостоятельно - делимся ссылкой на коллаб.

#python #pandas
🔥5
🔥8👍3
🔥 Поговорим о булевых столбцах в PostgreSQL

🤔 Небольшая предыстория. Мы обсуждали со студентами Симулятора «‎Аналитик данных»‎, что если столбец имеет булевый тип данных и нужно его указать в WHERE? Как можно изменить следующий запрос:

SELECT col1, col2, col3 
FROM table
WHERE col2 = True;

Надеемся, вы тоже знаете, что достаточно написать WHERE column2 если ищем значения True или WHERE NOT column2 если ищем значения False.

И тема развилась до того, что нашелся частный пример: имеется таблица, а в ней булевый столбец со значениями True, False и NULL. И если с выбором отдельных строк с True, False или NULL в данном случае все понятно. Просто пишем:

is null -- для выбора только строк со значениями NULL
= False / NOT boolean_column -- для выбра строк с False
= True / boolean_column -- для выбора строк с True;

🟠 То, что делать, если нужно достать строки, со значениями и False, и NULL?

Случился настоящий блиц! Были варианты:

SELECT * FROM table WHERE boolean_column is False or NULL;

Не сработает, так как интерпретируется как (boolean_column is False) or (null). Он возвращает только строки, где boolean_column равна False, так как второе условие всегда ложно.

SELECT * FROM table WHERE boolean_column is NULL or False;

Не сработает по той же причине. Интерпретируется как (boolean_column is Null) or (False).

Каков же правильный ответ?

Один из способов - использовать UNION двух запросов, которые по отдельности 100% работают:

SELECT * FROM table 
WHERE boolean_column IS NULL
UNION
SELECT * FROM table
WHERE NOT boolean_column

Можно еще попробовать неординарное COALESCE:

SELECT * FROM table
WHERE NOT COALESCE(boolean_column, False)

Этот запрос заменит все значения NULL на False, а затем выберет все значения False.

Еще один абсолютно рабочий вариант:

SELECT * FROM table
WHERE boolean_column is NULL
OR NOT boolean_column

💡 А на самом деле можно просто посмотреть на это с другой стороны:

В PostgreSQL есть 3 состояния для булевого столбца: True, False и неизвестность (NULL). Поэтому нам пригодится только запрос для NOT TRUE:

SELECT * FROM table
WHERE boolean_column IS NOT True;

💥 Не бойтесь выходить за рамки стандартного мышления, ведь верных решений может быть сколько угодно!

#sql
🔥19👍32
🔥 Переменная цикла и области видимости

Не так давно у нас был пост о том, что переменную цикла нельзя изменить внутри for-loop. Давайте продолжим тему переменной цикла, но взглянем на нее под другим углом!

🤔 Посмотрите на блоки кода ниже и ответьте, что вы получите после выполнения?

a = 1
for a in range(6):
pass

print(a)

------------------------

a = 1
[... for a in range(6)]

print(a)

Итак, в первом случае получим 5, а во втором - 1. То есть цикл for обновил существующую переменную a, но генератор списка этого не сделал. Итак, вопрос:

Почему переменная цикла обрабатывается по-разному в for-loops и list cоmprehension?

🟢 В цикле for переменная цикла как бы «утекает» в окружающую область видимости. Другими словами, как только цикл завершится, вы все равно сможете получить доступ к переменной цикла. Убедимся:

for loop_var in range(6):
pass

print(loop_var)
# 5

Мы могли бы написать даже что-то такое:

b = 1
for a in range(6):
pass

print(a, b)
# 5 1

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

🟠 Но генераторы списков так не работают. Вместо этого переменная цикла всегда остается локальной для генератора списка. Она никогда не утекает «наружу».

[... for loop_var2 in range(6)]

print(loop_var2)
# NameError: name 'loop_var2' is not defined

Вот почему существующая переменная a, которая также использовалась внутри генератора списка, осталась неизменной. Генератор списка определил переменную цикла (a), локальную для его области видимости.

#python
🔥10👍4🤩2
🔥 JOIN по неравенству

Часто вы используете в своей практике JOIN по неравенству? Думаем, не очень. Обычно все сводится к простому JOIN по равенству, вроде такого: ON t1.column = t2.column, верно?

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

SELECT s1.date, s1.revenue, 
SUM(s2.revenue) AS total
FROM sales s1
JOIN sales s2
ON s1.date >= s2.date
GROUP BY s1.revenue, s1.date
ORDER BY s1.date;

ON s1.date >= s2.date и есть - JOIN по неравенству.

Что еще можно использовать в таких JOINах? Все операторы сравнения: <, >, <=, >=, !=, и <> и оператор BETWEEN.

🟢 Давайте потренируемся!

У нас есть три таблицы в нашей воображаемой базе данных: houses, renters и deals. (См. в карточках 1-2 под постом)

Перечислим дома, которые мы можем предложить нашим арендаторам. Это должны быть дома (1) в предпочтительном районе, (2) в пределах их ценового диапазона, (3) с требуемым количеством спален и (4) не заняты (т.е. не указаны в нашей таблице сделок).

Вот такой запрос с JOIN по неравенству может решить нашу задачу: (См. в карточке 2)

🔵 Где нам еще может пригодится JOIN по неравенству?

Например, перечислим все дома из нашей базы данных вместе с датой соответствующей сделки (если она есть). Но берем даты только после 1 марта.

SELECT h.id, h.address, 
d.date
FROM houses h
LEFT JOIN deals d
ON h.id = d.house_id
WHERE d.date >= '2020-03-01';

(Результат запроса см. в карточке 3)

Мы получим только два дома, у которых есть даты сделки. А вот написав JOIN по неравенству, все сработает на отлично:

SELECT h.id, h.address, 
d.date
FROM houses h
LEFT JOIN deals d
ON h.id = d.house_id
AND d.date >= '2020-03-01';

(Результат запроса см. в карточке 4)

🔸 Такие JOINы могут быть очень полезными в разных сценариях, поделитесь с нами вашим опытом использования таких запросов. Часто ли вам приходится с ними сталкиваться?

#sql
🔥7👍51
🔥10👍4
💥 А вы знаете что такое RFM-анализ?

В работе аналитика часто встречается задача - провести RFM-анализ. Но иногда это задание вызывает трудности, поскольку студентам не так часто объясняют что такое RFM-анализ и с чем его едят, хотя это крайне полезный метод.

Давайте разберемся!

🔵 Что это такое?

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

RFM - расшифровка:

- Recency (давность): как давно клиент совершал заказы.
- Frequency (частота покупок): сколько раз клиент совершал заказы.
- Monetary (деньги): на какую сумму клиент совершал заказы.

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

🟣 Как это работает?

Суть RFM-анализа в том, что мы разделяем всех клиентов на группы, опираясь на то, как давно они сделали последнюю покупку, как часто покупали и насколько большой была сумма их заказов.

По каждому из этих признаков мы выделяем по три равные группы. Затем присваиваем каждой группе числовое обозначение от 1 до 3.

По давности заказа (recency):

- 1 — давние клиенты;
- 2 — сравнительно недавние клиенты;
- 3 — свежие клиенты.

По частоте покупок (frequency):

- 1 — покупает очень редко (единичные заказы);
- 2 — покупает нечасто;
- 3 — постоянный покупатель.

По сумме покупок (monetary):

- 1 — маленькая сумма;
- 2 — средняя сумма;
- 3 — большая сумма.

Например, клиент «111» покупал давно, один раз и на маленькую сумму. И скорее всего, нам не стоит за него бороться.

Но при этом клиентов «131», «132», «133» обязательно нужно попытаться вернуть - это наши уходящие постоянные клиенты с разным чеком. Отправляем им реактивационные письма, запрашиваем обратную связь, предлагаем бонусы, программы лояльности.

🔝Или пользователь «333»: покупает часто, на большую сумму и последняя покупка была недавно. Это наши лучшие клиенты. Таким клиентам следует уделять особое внимание, например, предлагать им эксклюзивные продукты или услуги, бонусы и скидки.

🟡 Что с этим делать?

С каждой группой можно строить отдельные коммуникации: давать им разную рекламу и делать разные email-рассылки. Например, группе постоянных VIP-клиентов высылать специальные предложения, а пользователям, которые давно не покупали — мотивирующую скидку, и настроить на них таргетированную рекламу.

⚙️ Границы давности, частоты и суммы покупок вы определяете самостоятельно. То есть, для какого-то бизнеса 10 000 руб. - это много для 1 клиента, а для какого-то - почти ничего. Или частота покупок на сайте авиабилетов будет существенно отличаться от частоты покупок в магазине «у дома». Поэтому здесь можете проявить фантазию, отталкиваясь от вашего бизнеса.

💡 Главное - помнить основной принцип: лучших клиентов мы стараемся удержать, средних «довести» до лучших, а уходящих и почти потерянных — вернуть. Потому что вернуть клиента всегда легче, чем привлечь нового!

А в следующем посте разберем как провести RFM-анализ на практике, с использованием SQL!

Приходите в наш 👉 Симулятор "Аналитика данных" 👈, чтобы точно знать как вести себя в боевых задачах!

#analytics
👍17🔥62
💥 RFM-анализ с помощью SQL

Сегодня мы с вами вместе пройдем по шагам и сделаем RFM-анализ для магазина «у дома».

У нас есть таблица orders с данными о покупках за последние 90 дней (мы нагенерировали случайных клиентов и заказов).  (см. Карточку 1)

Приступим!

⚙️
Порой бывает полезно ограничить RFM-анализ конкретными временными рамками. Например, смотреть только последний год, или только последние 3 месяца. В таких случаях не забывайте добавить WHERE условие. В нашем же случае 3 месяца - как раз подходящий срок, чтобы проследить ушедших клиентов и частоту покупок. 

🔵 Итак, для вычисления Recency мы должны выяснить дату последнего заказа клиента. Для Frequency добавим простой count по столбцу order_id. А Monetary получим, взяв среднее по столбцу order_amount. (см. Карточку 2)

🤔 Теперь, когда мы имеем подготовленные данные - можем сформировать сегменты. Тут придется поразмыслить.

🟣 Какова адекватная давность покупок в магазине «у дома»? Достаточно часто людям приходится что-то приобрести, даже если они не закупаются в этом магазине на месяц. Но взять условные 7 дней, пожалуй, некорректно, так как люди могут уезжать в отпуск, на дачу и тд. При этом не превращаясь в «потерянных». Поэтому обозначим условные 30 дней - постоянные покупатели, до 60 - уходящие, и более 60, скорее всего потерянные клиенты (переехавшие, или выбравшие новый, открывшийся в районе магазин 🙂).

🟡 Что насчет Frequency? Тут свобода для фантазии. На наших данных мы определили более 5 покупок - частые, более 3 - средние, и до 3 покупок - редкие. Вероятно, если бы это были настоящие заказы, можно было бы взять окна побольше, поскольку 5 покупок в магазине у дома за 90 дней - не выглядят как максимально частый клиент 🙂.

🟢 И, наконец, Monetary - у нас все покупки до 10 000 рублей. Соответственно, разделим их примерно так: свыше 5 000 - высокий чек, выше 2 000 - средний, и все, что ниже - низкий. 

И теперь напишем следующий запрос: (см. Карточки 3-4)

💡 Уже прослеживается распределение. Большинство клиентов являются частыми покупателями. Покупатель 111 явно не из нашего района - случайная покупка. И в целом, получается у нас очень хороший магазин - много 333, много 322. Теперь с каждой группой можно настраивать различные коммуникации.

🔴 Важный момент: на нашем симуляторе есть большое практическое задание на проведение RFM-анализа. И мы видели варианты решения «в лоб» с использованием NTILE.

Что делает такое решение? NTILE просто разделяет данные на максимально возможно равные сегменты, не опираясь на данные в этих сегментах.

Например попробуем на наших данных разделить каждую группу на 3. (см. Карточки 5-6)

Выглядит будто бы лучше, у нас больше двоек и единиц, и будто бы получается честнее. НО! Если посмотреть подробнее, клиент, покупавший у нас 25 дней назад - отнесен к потерянным, при этом он был покупателем частым и с высоким чеком. Вполне вероятно, что это отнесение несколько ошибочно (он мог просто уехать в отпуск). Аналогично, клиентов с 5 покупками отнесли к наименее частым - не всегда это будет корректно.

В целом, такое решение тоже может работать, чтобы предварительно посмотреть сегментацию, но если вы хотите провести качественный RFM-анализ крайне важно адаптировать его под ваш бизнес.

#analytics #sql
🔥11👍64
🔥231👍1