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

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

Наш уютный чат: @itresume_chat
Поддержка: @simulative_support
Download Telegram
🔥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
💥 3 лайфхака по считыванию csv-файла в Python 💥

Нам, аналитикам, часто приходится работать с csv-файлами. Все-таки это удобный способ хранения больших наборов данных и дальнейшей работы с ними.

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

🔵 Первый лайфхак

Иногда при больших исходных данных работать приходится только с некоторым количеством из них. И если заранее известно, что из условно 10 миллионов строк вам будет достаточно только 1000, задайте в read_csv параметр nrows. Это позволит сэкономить и память, и время.

В противном же случае пришлось бы выполнить сразу несколько действий: загрузить весь сет, взять от него срезом только первую 1000 строк, и удалить первоначальный. Один параметр вместо трех строк. Удобно!

#Было
df = pd.read_csv('file.csv')
new_df = df[:1000].copy()
del df
_______
#Стало
df = pd.read_csv('file.csv',
nrows = 1000)

🟣 Второй лайфхак - использование параметра usecols. Опять же, большие файлы часто содержат много столбцов, которые не всегда будут необходимы. Они и мешают просматривать датафрейм, и загружают память. В случаях, когда для работы вам будет достаточно только некоторого подмножества - укажите необходимые столбы в usecols, и это значительно сэкономит время обработки.

Опять же, в противном случае пришлось бы загрузить весь датафрейм, сформировать новый только из подходящих колонок или дропнуть ненужные колонки в исходном датафрейме. Это привычный способ, но теперь вы знаете, как сделать быстрее)

#Было
df = pd.read_csv('file.csv')
df = df[['col1', 'col2',
'col3']]
________
#Стало
df = pd.read_csv('file.csv',
usecols = ['col1', 'col2',
'col3'])

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

То есть если вы передадите список [0, 1, 2], то будут пропущены первые три строки:

df = pd.read_csv('file.csv', 
skiprows = [0, 1, 2])

💡 Более того, вы можете не просто пропустить несколько идущих подряд строк, вы можете передать в skiprows функцию, которая определит, какие строки необходимо сохранить, а какие можно пропустить. 

Например, известно, что в файле каждая вторая строка пустая, просто передайте в skiprows лямбда-функцию:

df = pd.read_csv('file.csv', 
skiprows=lambda x:
(x % 2) != 0))

В нашем 👉 Симуляторе «Аналитик данных» 👈 в модуле по Python эти уловки вам точно пригодятся, ведь вы будете работать с файлами (логами, выгрузками и таблицами) из реального бизнеса от наших партнеров!

#python #pandas
🔥17👍8
🔥 А вы часто используете оператор UNION в SQL?

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

Поэтому предлагаем познакомиться поближе с этим оператором.

🟢 Как представить себе UNION?

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

Допустим, у нас есть две таблицы employees и managers, содержащие следующую информацию:

Таблица employees:
| id | name | age | salary |
|----|------|-----|--------|
| 1 |Alice | 25 | 40.000 |
| 2 |Bob | 30 | 50.000 |
| 3 |Eve | 35 | 60.000 |

Таблица managers:

| id | name | age |  depart   |
|----|------|-----|-----------|
| 4 |Ben | 40 | Sales |
| 5 |David | 45 | Marketing |

Чтобы объединить эти таблицы, мы можем использовать оператор UNION:

SELECT id, name, age
FROM employees
UNION
SELECT id, name, age
FROM managers;

Результатом будет следующая таблица:

| id | name | age |
|----|------|-----|
| 1 |Alice | 25 |
| 2 |Bob | 30 |
| 3 |Eve | 35 |
| 4 |Ben | 40 |
| 5 |David | 45 |

Если бы в таблицах были дубликаты строк, то они бы удалились. Если нам нужно сохранить дубликаты, то следует использовать оператор UNION ALL.

🔵 Пара моментов, заслуживающих внимания:

1. Как отмечалось ранее, общее количество колонок в обоих запросах с UNION должно быть одинаково. Равно как и тип данных этих колонок.

2. Поскольку UNION не гарантирует нам никакого конкретного порядка строк - необходимо поставить ORDER BY в самый конец вашего запроса, чтобы строки шли по порядку:

SELECT id, name, age
FROM employees
UNION
SELECT id, name, age
FROM managers
ORDER BY 1;

🤔 Уже есть идеи, в каких прикладных задачах не обойтись без оператора UNION?

#sql
👍9🔥8
🔥 Избегайте этого при работе с Pandas

Как вы выбираете элементы при работе с датафреймами? Сначала строку, а потом столбец или столбец, а за ним строка? А может вы вовсе определяете порядок наугад? Если так, то вы упускаете огромный потенциал производительности! Скорее читайте наш пост.

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

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

# сначала выбираем столбец, затем строку
%timeit df['col']['row']
#2.96 µs

# наоборот - строка, затем столбец
%timeit df.loc['row']['col']
#45.4 µs

Почему это происходит?

Pandas DataFrame - это структура данных, состоящая из столбцов, и последовательные элементы в каждом столбце хранятся рядом друг с другом в памяти.

И поскольку процессоры умеют эффективно работать со смежными блоками памяти, доступ к столбцу осуществляется намного быстрее, чем к строке.

# выбираем столбец (смежные блоки памяти) 
%timeit df['my_column']
# 1.73 µs

# выбираем строку (несмежные)
%timeit df.iloc[0]
# 38.4 µs

В примере выше, извлечение более 32 миллионов элементов столбца оказалось более чем в 20 раз быстрее, чем извлечение всего девяти элементов, хранящихся в строке.

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

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

Так что запоминайте: сначала столбец, а потом строка 💡


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

#python #pandas
🔥22👍8
🔥 Задача с собеседования по SQL

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

🔹 Итак, условие!

Есть таблица table_A. Необходимо перевернуть её таким образом, чтобы id оставались в том же порядке, но значения столбца N были в обратном порядке.

 table_A          Result

| id | N | | id | N |
|----|---| |----|---|
| 1 | A | | 1 | F |
| 2 | B | | 2 | E |
| 3 | C | ---> | 3 | D |
| 4 | D | | 4 | C |
| 5 | E | | 5 | B |
| 6 | F | | 6 | A |

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

🤔 Согласитесь, тут очень напрашивается JOIN, но как бы его сделать?

А ведь если бы мы имели такую же табличку с колонкой rev (обратные индексы), например, мы могли бы по ней как раз сделать JOIN. По условию t1.id = t2.rev. Например, как здесь:

 table_A          table_B

| id | N | | id | N |
rev
|
|----|---| |----|---|----|
| 1 | A | | 1 | A | 6 |
| 2 | B | | 2 | B | 5 |
| 3 | C |-->| 3 | C | 4 |
| 4 | D | | 4 | D | 3 |
| 5 | E | | 5 | E | 2 |
| 6 | F | | 6 | F | 1 |

Как создать rev? Воспользуемся оконной функцией ROW_NUMBER, пронумеровав все строки в обратном от столбца id порядке!

Теперь мы можем написать запрос:

SELECT a.id, b.N
FROM table_A a
INNER JOIN (
SELECT *,
ROW_NUMBER() over
(order by id DESC) as rev
FROM table_A
) b
ON a.id = b.rev

🔸 И получаем то, что нам и необходимо:

| id | N |
|----|---|
| 1 | F |
| 2 | E |
| 3 | D |
| 4 | C |
| 5 | B |
| 6 | A |


🔻 Кстати, если вы хотите подготовиться к собеседованию по SQL или Python, заходите на нашу платформу 👉 IT Resume 👈! Там вы найдете много полезного 😉

#sql #interview_problems
🔥22👍62
🔥 А вы активно используете match/case в Python?

Это достаточно новая конструкция, введенная в Python 3.10, которая позволяет нам написать более читаемый и лаконичный код при обработке различных вариантов значений. Из-за ее «молодости» не все о ней знают, но она может быть очень полезна и даже удобнее, чем if/else. Давайте проверим!

🟢 Что такое match/case?

Это способ сопоставления значения с набором шаблонов и выполнения соответствующего блока кода для первого подходящего шаблона. Подобное по функциональности можно встретить в других языках программирования под названием switch/case. Однако в Python match/case стал еще мощнее и гибче.

Давайте рассмотрим простой пример использования:

def http_status(status_code):
match status_code:
case 200:
return "OK"
case 400:
return "Bad Request"
case 404:
return "Not Found"
case 500:
return "Internal
Server Error"
case _:
return "Unknown Status"

print(http_status(200))
# OK

print(http_status(400))
# Bad Request

print(http_status(404))
# Not Found

print(http_status(500))
# Internal Server Error

print(http_status(403))
# Unknown Status

Здесь мы определяем функцию http_status, которая принимает аргумент status_code. С помощью конструкции match мы проверяем значение status_code и возвращаем соответствующий текстовый статус для первого подходящего шаблона. Если ни один из шаблонов не подходит, мы используем шаблон _, чтобы вернуть "Unknown Status".

🔵 А что такое шаблон _?

Шаблон _ представляет собой своего рода «по умолчанию» в конструкции match/case. Если ни один из описанных шаблонов не совпадает с переданным значением, то выполняется блок кода, соответствующий шаблону _. Это позволяет предусмотреть обработку всех возможных вариантов, даже тех, которые не перечислены явно.

Согласитесь, что match/case делает наш код более легким для чтения. Мы можем сразу видеть все возможные варианты значений и обрабатывать их отдельно.

🌟 И это только начало! Match/case в Python имеет еще много интересных возможностей, которые мы рассмотрим в следующем посте. Он позволяет избежать громоздких проверок типов данных, длины и других условий, делая наш код гораздо более компактным!

А вы уже пробовали использовать match/case в Python? Расскажите о своем опыте в комментариях!

#python
🔥16👍53
🔥 Что еще может match/case в Python

Продолжаем нашу серию постов о match/case! Здорово, что вам так понравилась эта конструкция! Кто-то уже оказался знаком с ней, а для кого-то это стало новым знанием.

В предыдущем посте мы рассмотрели простой пример использования match/case и, как верно заметили наши подписчики, его действительно можно заменить и dict.get() и вполне сносным if/else. Но возможности этой конструкции не ограничиваются только этим.

🟣 Давайте рассмотрим более сложный случай, в котором использование if/else становится неудобным.

Представьте, что у нас есть функция make_point, которая принимает на вход координаты точки. Наша задача - создать объект класса Point3D и инициализировать его соответствующими значениями.

Для начала определим класс:

from dataclasses import dataclass

@dataclass
class Point3D:
x: int
y: int
z: int

📝 А теперь напишем нашу функцию с использованием if/else.

def make_point(point):
# Проверяем тип
if isinstance(point,
(tuple, list)):
# Проверяем длину
if len(point) == 2:
x, y = point
return Point3D(x, y, 0)
elif len(point) == 3:
x, y, z = point
return Point3D(x, y, z)
else:
raise TypeError("Unsupported")
else:
raise TypeError("Unsupported")

❗️ Здесь мы пишем достаточно сложную цепочку вложенных операторов, а также явно проверяем тип, длину и распаковываем переменные. Это усложняет чтение и понимание кода.

Но используя match/case мы можем написать более чистый и выразительный код:

def make_point(point):
match point:
case (x, y):
return Point3D(x, y, 0)
case (x, y, z):
return Point3D(x, y, z)
case _:
raise
TypeError("Unsupported")

👉 В первом case мы сопоставляем точку с двумя элементами (x, y) и создаем объект Point3D с координатами (x, y, 0). Во втором case сопоставляем точку с тремя элементами (x, y, z) и создаем объект Point3D с указанными координатами. В случае, если ни одно из сопоставлений не произошло, мы генерируем исключение TypeError.

🔍 Протестируем работу функции make_point:

make_point((1, 2))
# Point3D(x=1, y=2, z=0)

make_point([1, 2, 3])
# Point3D(x=1, y=2, z=3)

make_point((1, 2, 3, 4))
# TypeError: Unsupported

Как видите, результаты будут одинаковыми для обоих вариантов написания функции. Но благодаря match/case нам не нужно явно проверять тип, длину и распаковывать переменные. Это делает код гораздо более понятным и удобным для чтения.

🌟 А если вам интересно, мы можем рассказать о том, как работать со словарями и списками в match/case. Хотите узнать больше?

Проверьте, можно ли решить 👉 задачу 👈 с использованием конструкции match/case!
🔥16👍1
🔥 Новые крутые фичи на платформе IT Resume

На нашей платформе с задачами по программированию IT Resume произошли большие обновления, и мы спешим с вами поделиться ☺️

💥 Во-первых, вот несколько новых крутых задач, которые вам точно стоит порешать:

[Тестовое Тинькофф] Отчет о зарплате

Это задача по SQL из тестового задания в Тинькофф на позицию Аналитика. Кстати, на платформе есть еще несколько задач из этого цикла - некоторые совсем простые, а над некоторыми придется крепко подумать.

🔗 Ссылка на задачу: ссылка


Сделать из широкой таблицы длинную

Классическая задача на pivot-unpivot в SQL. Недавно сразу 2 человека сказали нам, что встретили эту задачу на собеседованиях в разные компании, поэтому мы решили включить ее сегодня в подборку - обязательно порешайте!

🔗 Ссылка на задачу: ссылка


Поиск опасности

Типичная алгоритмическая задача на Python, которые очень часто встречаются на технических секциях собеседований в любые компании.

🔗 Ссылка на задачу: ссылка


💥 Во-вторых, за последнее время мы внедрили много крутейшего функционала и теперь решать задачи стало еще приятней:

Решения других пользователей

Вы много просили об этом, поэтому мы добавили раздел с решениями других пользователей 😍 Теперь вы не просто можете порешать задачу и посмотреть на эталонное решение, но и познакомиться с решениями других пользователей!

Цветовые схемы в редакторе кода

Теперь вы можете выбрать одну из 4 цветовых тем в редакторе кода, когда решаете задачи. Если любите темную тему - попробуйте cobalt или material, а если светлую - вам зайдет 3024-day.

Шорткаты и автокомплит

Да, долгожданная фича - теперь вы можете отправлять код на проверку/выполнение нажатием одной клавиши, а также воспользоваться автоматическим дополнением кода 🔥

Так что обязательно заглядывайте к нам, чтобы подготовиться к собеседованию, подкачать свой скилл да и просто порешать задачки 😉

🔗 Ссылка на платформу 👉 ссылка 👈
🔥105👍3
🔥 SQL vs Excel

Мы частенько встречаем в требованиях к аналитику - умение пользоваться Excel. Это действительно широко используемый инструмент работы с данными. Но когда дело доходит до сложных анализов и больших объемов данных, есть более удобный и иногда незаменимый инструмент – SQL.

Давайте проведем небольшое сравнение некоторых агрегатных функций в Excel и SQL, и узнаем, почему изучение SQL имеет смысл. Возможно, вы даже сможете доказать работодателю, что ваши навыки SQL круче и ценнее, чем знание Excel!

🟢 COUNT, SUM, AVG

Excel – рабочая лошадка, этот инструмент хорош в вопросах подсчета, суммирования и вычисления среднего. Но когда работать приходится даже с 5000+ строками, он сдает позиции. А для SQL это не проблема, он может сделать те же вычисления и объемы его не пугают:

SELECT COUNT(col) AS count_row,
SUM(col) AS sum_total,
AVG(col) AS avg_total
FROM table
;


С SQL вы получите результаты быстро даже для миллионов записей. Чистая магия! 🔮

🔵 GROUP BY

Одной из ключевых особенностей SQL является группировка данных с помощью GROUP BY. В Excel это можно реализовать с помощью сложных формул или использования сводных таблиц, но это требует куда больше усилий и времени. А SQL легко группирует ваши данные и применяет агрегатные функции к каждой группе:

SELECT category_name, COUNT(*) 
AS count_products
FROM products
GROUP BY category_name;

Удобно, правда?

🟣 Оконные функции

Вот где SQL действительно выделяется. Оконные функции позволяют нам делать вычисления на основе определенного окна данных, а не только по группам, как это делается в Excel. Например, мы можем вычислять скользящее среднее или найти топ-значения в определенном окне. В Excel это потребовало бы использования сложных формул или макросов, в то время как в SQL оконные функции - встроенная возможность.

SELECT date,
value,
AVG(value) OVER (
ORDER BY date ROWS
BETWEEN 2 PRECEDING
AND CURRENT ROW)
AS rolling_average
FROM table_name;

Быстро, легко и главное – коротко!

Итак, можно с уверенностью сказать, что Excel – неплохой выбор для небольших объемов данных, но SQL – это босс. Изучив SQL вы сделаете свою аналитику эффективной и удобной! Поэтому время начать учить SQL и перейти на новый уровень аналитической мощи! 💥

📢 Приглашаем вас на наш Симулятор «Аналитик данных», где мы не просто рассказываем теорию по SQL, Python, продуктовым метрикам и другим темам, но и даем много задач из реальной работы аналитика!

#sql
🔥7👍41
🔍 Быстро и надежно: Избегаем проблем с NumPy! 🔍

Аналитики знают, что NumPy - это мощный инструмент для численных вычислений. Однако, существует одна тонкость, о которой стоит помнить.

Если в массиве NumPy присутствует значение NaN (Not a Number), некоторые агрегатные функции могут вернуть неожиданный результат. И это может оказаться совсем не то, чего вы ожидаете!

import numpy as np
arr = np.array([1, 2, 3, np.NaN])
np.sum(arr) # nan
np.mean(arr) # nan
np.max(arr) # nan

Но не беспокойтесь, у нас есть решение! 👇

🔵 Во-первых, хорошей практикой является проверка массива на наличие значений NaN и их замена, например, на 0. Это поможет избежать проблем в большинстве случаев.

🟠 Однако, есть ещё один способ избежать проблем, который предлагает сам NumPy. Для тех, кто хочет быть на высоте, существуют специальные методы, нечувствительные к значениям NaN! 🚀

Например, вместо использования функций np.sum, np.mean, np.max и др., вы можете воспользоваться np.nansum, np.nanmean, np.nanmax и другими аналогичными методами.

import numpy as np
arr = np.array([1, 2, 3, np.NaN])
np.nansum(arr) # 6.0
np.nanmean(arr) # 2.0
np.nanmax(arr) # 3.0

Их особенностью является то, что они игнорируют значения NaN в массиве и возвращают результат, как будто эти значения не существуют.

Теперь, даже если где-то в вашем массиве присутствует хотя бы одно значение NaN, это уже не повлияет на все остальные вычисления! Вы получите верные и надежные результаты! 💪

💬 Пишите в комментариях, если вам пригодился этот совет или если у вас есть свои хитрости по работе с NumPy! 📚

#python #numpy
🔥24👍61