четверг, 30 мая 2024 г.

Some words about server container GTM

 https://business.reddithelp.com/helpcenter/s/article/Set-Up-a-Web-Container

With a client-side GTM setup, you can track tags in your website's code. Plus, it makes it simpler to handle consent, so it’s easier to follow the rules and respect what users want with their data.

However, potential limitations like browser-based privacy measures or ad-blocking extensions can impact data collection accuracy. Server-side tagging can potentially mitigate client-side measurement blocking.

Learn how to integrate your web container into the server container - https://business.reddithelp.com/helpcenter/s/article/Set-Up-a-Server-Container

A server container reduces inaccuracies by avoiding client-side 3rd-party blocking, like browser-based privacy measures or ad-blocking extensions. On top of that, it lets you establish consent management mechanisms, ensuring your data collection practices align with regulatory requirements and user preferences.

While setting up a server GTM container improves the reliability and consistency of conversion metrics, the setup process can be a little complicated. We suggest reaching out to developers if you're unsure and giving the setup a good test run to ensure it’s working as best as it can.

https://business.reddithelp.com/helpcenter/s/article/event-deduplication

Replace some href with GTM example

<script>

var chnglinks = document.querySelectorAll('a[href="/"]');

var currentUrl = window.location.href;

chnglinks.forEach(function(link){

    link.setAttribute("href", currentUrl);

});

</script>

воскресенье, 12 мая 2024 г.

Git cложное слияние

http://uneex.org/LecturesCMC/PythonDevelopment2023/04_MergetoolCommandline

https://youtu.be/uVh3BEL1iyU

Вообще merge commit опасная штука, их плодить не надо т.к. могут быть конфликты, когда редактируется одно и то же место, которое правится вручную при merge commit

Сложное слияние

При merge и rebase могут возникать конфликты: в двух историях изменён один и тот же контекст: 

  • Создадим заведомо конфликтующий коммиты на двух ветках 
    $ git init
    Initialized empty Git repository in /home/george/example/.git/
    $ git add .
    $ git commit -a -m "Initial commit"
    [master (root-commit) 8ab1be9] Initial commit
     1 file changed, 63 insertions(+)
     create mode 100644 keyword.py
    $ git branch second
    $ git branch
    * master
      second
    $ grep -Ev "except|False" /usr/lib64/python3.10/keyword.py > keyword.py
    $ git diff
    diff --git a/keyword.py b/keyword.py
    index cc2b46b..9f30ffb 100644
    --- a/keyword.py
    +++ b/keyword.py
    @@ -16,7 +16,6 @@ Alternatively, you can run 'make regen-keyword'.
     __all__ = ["iskeyword", "issoftkeyword", "kwlist", "softkwlist"]
    
     kwlist = [
    -    'False',
         'None',
         'True',
         'and',
    @@ -31,7 +30,6 @@ kwlist = [
         'del',
         'elif',
         'else',
    -    'except',
         'finally',
         'for',
         'from',
    $ git commit -a -m "False+except"
    [master f1fbdeb] False+except
     1 file changed, 2 deletions(-)
    $ git checkout second
    Switched to branch 'second'
    $ grep -Ev "finally|yield" /usr/lib64/python3.10/keyword.py > keyword.py
    $ git diff
    diff --git a/keyword.py b/keyword.py
    index cc2b46b..251bd3a 100644
    --- a/keyword.py
    +++ b/keyword.py
    @@ -32,7 +32,6 @@ kwlist = [
         'elif',
         'else',
         'except',
    -    'finally',
         'for',
         'from',
         'global',
    @@ -50,7 +49,6 @@ kwlist = [
         'try',
         'while',
         'with',
    -    'yield'
     ]
    
     softkwlist = [
    $ git commit -a -m "finally+yield"
    [second 0804e39] finally+yield
     1 file changed, 2 deletions(-)
    $ git log --graph --pretty=oneline --abbrev-commit --all
    * 0804e39 (HEAD -> second) finally+yield
    | * f1fbdeb (master) False+except
    |/
    * 8ab1be9 Initial commit
    
    
  • Итак, у нас есть три состояния файла keyword.py

    1. 8ab1be9 (общий предок) 

    2. f1fbdeb (на ветке master) — без False и except

    3. 0804e39 (на ветке second) — без finally и yield

  • Контекст изменений для except и finally пересекается 

    • ⇒ при слиянии будут конфликты 
  • Попробуем объединить: 
       1 $ git branch
       2   master
       3 * second
       4 $ git merge master
       5 Auto-merging keyword.py
       6 CONFLICT (content): Merge conflict in keyword.py
       7 Automatic merge failed; fix conflicts and then commit the result.
       8 $ grep -EC3 "<<<<|====|>>>>" keyword.py
       9     'del',
      10     'elif',
      11     'else',
      12 <<<<<<< HEAD
      13     'except',
      14 =======
      15     'finally',
      16 >>>>>>> master
      17     'for',
      18     'from',
      19     'global',
      20 
    
  • Часть изменений применены (про False и про yield), потому что контексты не пересекались, часть (про except и finally) — нет. 

  • Файл содержит вставки вида: 
    <<<<<<< HEAD
    =======
    >>>>>>> master
  • Это т. н. 3-way diff по схеме «общий предок + конфликтующие изменения» 
    • Все неконфликтующие изменения из обеих веток применены 

    • HEAD — это содержимое текущей ветка, master — с чем мержим 

      • {1} было бы неплохо ещё знать, что раньше-то было, но тут не показывается 

    • Все "<<<<<<<""=======" и ">>>>>>>" надо убрать (и ненужные изменения тоже) 

    • Получится merge commit с изменением, неравным тому, что делалось на ветках 
  • Если вас удовлетворяют изменения, проделанные на ветке master, можно просто git checkout master keyword.py<!> но тогда пропадут все изменения, включая уже применённые

  • Когда всё готово, делаем git commit -a

   1 $ vim keyword.py
   2 
   3 $ git commit -a
   4 [second 6568682] Merge branch 'master' into second
   5 $ git log --graph --pretty=oneline --abbrev-commit --all
   6 *   6568682 (HEAD -> second) Merge branch 'master' into second
   7 |\
   8 | * f1fbdeb (master) False+except
   9 * | 0804e39 finally+yield
  10 |/
  11 * 8ab1be9 Initial commit
  12 
  • Если в историях больше одного коммита, merge надо продолжить с помощью git merge --continue

  • Если вы окончательно запутались (особенно в многокоммитных мержах), всё можно откатить назад с помощью git merge --abort



    Mergetool

    Инструмент, в котором есть {1} , имеет общее название «merge tool». 

    • Список: git mergetool --tool-help

    • Запускается вместо ручного исправления конфликтов 
    • *vimdiff показывает четыре окна: 

      • «Эта» ветка (LOCAL) 

        Общий предок (BASE) 

        «Та» ветка (REMOTE)

        Файл с конфликтами (его и надо исправлять)

      • Могут остаться backup-файлы, их надо удалить git clean -f

    • Другие утилиты позволяют «накликивать» изменения) 

    Пример на том же репозитории: 

    • просто удалим merge-коммит (git reset --hard HEAD~

    • вызовем git merge

    • вызовем git merge-tool --tool=gvimdiff (или meld)

      • :diffget RE  " get from REMOTE
        :diffget BA  " get from BASE
        :diffget LO  " get from LOCAL

    Для постоянного вызова правильного mergetool: 

    • $ git config --global merge.tool ваш_mergetool


Git метки (тэги)


Метки (теги)

  • Указатели на коммиты, лежат в .git/refs/tags/
  • Выступают в роли commit-ish (как commit ID, ветки и ссылки относительно HEAD)
  • Можно запушить с ключом --tags (но по умолчанию локальны)
  • Аннотированный тег сопровождается специальным объектом-тегом в .git/objects/**/
    • git tag [commitish] -a тег -m Аннотация
    • Можно подписывать электронной подписью 
Две роли тегов: информационная и управляющая (особенно подписанных)

find . > /tmp/files_before
git tag NewDate [commit id]
пометили тегом коммит

git tag

покажет существующие метки, чтобы вывести в консоль можно убрать пейджер git config core.pager ""

git log --graph --pretty=oneline --abbrev-commit --all
 метки можно посмотреть на графе

find . > /tmp/files_after

diff /tmp/files_before /tmp/files_after

покажет нам новый файл, который появился в связи с созданием метки .git/refs/tags/NewDate в этом файле просто commit id

git checkout -b old NewDate

переключаемся на ветку old с одновременным ее созданием на коммите с меткой

Аннотированные теги:

 теги сопровождающиеся специальным объектом, этот объект можно подписать электронной подписью

git tag Anno -a -m "tag message here"

-a добавляет аннотированный тег, -m добавляет message

git tag -d Anno

удаляет тег


Аннотированный тег создает специальный объект в .git/objects/  вида tag (четвертый тип в дополнение к blob, tree, commit), в котором хранится  commit id, название тэга, автор и commit message.

 

 

 

 

суббота, 11 мая 2024 г.

Git ветки

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

Здесь разберем случае когда изменения не будут конфликтовать друг с другом.

git branch new
создали новую ветку new

git branch

покажет какие ветки есть в наличие, звездочкой помечена активная ветка

допустим мы накоммитили в ветку master, ветка new при этом находится в той точке где она и была заведена

git checkout new

сменили активную ветку, теперь в git branch звездочкой будет помечена ветка new

и теперь накоммитим сюда. только создадим другие файлы чтобы изменения не конфликтовали. На этом этапе у нас есть две ветки разработки у которых где-то в прошлом есть общий коммит. Но на текущий момент это две параллельные ветки. Когда мы говорим git branch, то в каталоге .git/refs/heads заводится еще один файл с названием как у ветки, в котором содержится commit id на вершину нового фронта разработки

Когда используют ветки:

  • совместная разработка
  • схема devel - testing - production
  • разработка временно ухудшающая качество
git checkout master
git merge new (можно указать commit id указав некоторую точку в дереве с которой хотим померджиться)

операция merge сопровождается специальным commit'ом, который называется merge commit



git reflog

показывает все коммиты в том числе и потерянные пока мы не сделаем repack 

вместо команды merge можно было сделать:
git checkout new
git rebase master
это rebase new поверх master


git checkout master

git merge new

здесь merge commit уже не создается потому что история после rebase стала линейная нам нечего сливать, мы просто подвигаем фронт под названием master к тому месту где был фронт под названием new

 

git rebase --interactive HEAD~5

интерактивный rebase последних 5 коммитов. rebase это переписывание истории его лучше делать на том что мы еще не запушили, если мы rebase' нули то что уже запушили нам потом придется  делать force push и те люди которые с вами синхронизируются пострадают.

 

Git удаленный репозиторий

mkdir repo.bare

cd repo.bare

git init --bare

инициализирует репозиторий прямо в текущем каталоге без подкаталога .git Это репозиторий, у которого нету рабочей копии.

cd ..

git clone repo.bare repo

 команда приведет к клонированию пустого репозитория.

cd repo

git status

git remote -v 

теперь у нас есть удаленный репозитория по адресам в repo.bare

date >> initial

git add initial

git commit -m "Initial commit"

git log

git push 

Запушили на "удаленный" репозиторий

далее происходит работа команды с удаленным репозиторием или это может быть работа с другого компьютера

чтобы синхронизироваться с удаленным репозиторием делаем

git pull


Правила оформления commit'ов:

  • одно изменение один commit
    • изменение это решение какой-то одной задачи, багфикс, новая фитча, редизайн
    • если задача слишком большая ее следует разбить на подзадачи в отдельных commits
    • если еще больше или одновременно с другими задачами, то ведется работа с отдельными ветками branches
  • commit не ломает уже имеющихся свойств, приложение продолжает работать не хуже чем раньше
    • если предполагается что ближайшая серия коммитов приведет к потере функциональности то лучше сделать отдельную ветку
Если мы еще не опубликовали, то переписывать историю можно как угодно (все что между HEAD и origin):

cp /bin/bash .
git add bash
git commit -m "False commit"
git log -p
закоммитили фигню, но пока не опубликовали

git status

расскажет о том что есть один не опубликованный коммит

git reset --hard [commit id] (либо git reset --hard HEAD^)

если не сказать --hard мы откатимся в репозитории на предыдущее состояние, а рабочая копия останется какая и была. Как если бы мы эти изменения сделали, но не закоммитили.

--hard синхронизирует рабочую копию тоже

 

commit id (sha-1) вычисляется от всего содержимого и даже от commit message, поэтому если отредактировать message это будет новый commit.

git commit -amend
используем если нужно исправить последний кормит. Фактически мы перекомичиваем тот же самый комит (последний). операция сохраняет время коммита

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


 

Git локальный репозиторий

https://git-scm.com/book/en/v2 
Нужно читать от начала и до конца




Система контроля версий: 
  • Хранение 
  • Версионирование 
    • Файлов/объектов
    • Состояний всего корпуса кода
  • История изменений 
    • Возможно, нелинейная (орграф, точнее — сеть)
  • Создание информационного пространства вокруг исходного текста


mkdir newrepo
git init
в рабочей копии нет ни одного файла (вообще), но есть каталог .git, который является локальным полноценным git хранилищем

cal > cal.txt
git status
git нам скажет что у нас в каталоге среди рабочей копии появился файл и он Untracked. 

git add cal.txt (git add -A)

git status

скажет нам что файл добавлен к отслеживанию (changes to be committed)  

git commit -m "new calendar file"

git status

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

git log 

отобразит commit id и commit message

git config core.editor 'ваш любимый редактор'
может поменять редактор

git config --list

отобразит список настроек которые могут быть local и global. git config --local ... // git config --global ... // git config --local --unset --user.name

echo "change file" >>  cal.txt

git status

знает что этот файл уже был добавлен в отслеживание и что он сейчас изменен но пока not staged for commit. Мы можем выбирать какие изменения в файле добавить в коммит. пока добавим все

git add cal.txt

git commit -m "new changes"

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



Историю коммитов можно посмотреть командой git log. Для того, чтобы отключить постраничный просмотр, используйте git -P log.

Все объекты имеют уникальный ID (это SHA-1 хеш). Объект с ID, допустим, 8dccc7a1d248ea923156b2e762e576b44e07886a, хранятся в файле с именем .git/objects/8d/ccc7a1d248ea923156b2e762e576b44e07886a
Посмотреть содержимое объектов можно (но непонятно, зачем ☺), например, так:

python3 -c "import sys; import zlib; print(zlib.decompress(sys.stdin.buffer.read()))" < .git/objects/8d/ccc7a1d248ea923156b2e762e576b44e07886a

В этих объектах мы найдем:
  • blob - это собственно наш файл в репозитории. Состояние файла на какой-то момент это не кусок файла не патч это именно файл целиком
  • состояния файлов на какой-то момент объединяются в дерево tree. Дерево говорит, в вашей ветке разработки на данный момент присутствовали вот такие файлы (размер, права, имя файла и id)
  • и собственно commit. Идентификатор дерева, id родительского коммита, имя автора и commit message

Эти три объекта создаются при каждом новом коммите. На каждый файл создается новый blob.
История разработки выглядит как цепочка коммитов.


  • для просмотра веток удобен qgit (или git log --graph --pretty=oneline --abbrev-commit --all)
  • не обязательно складывать в один коммит все что нахакали, можно разделить на разные

Для того чтобы сбросить коммиты (по правилу хорошего тона, те что не опубликовали чтобы избежать  git push --force) можно использовать git reset [commit id]. Эта команда откатит коммиты, но оставит рабочую копию не тронутой (изменения сохранятся).


Как разбить одну большую пачку изменений в несколько коммитов

git diff
смотрим все изменения

 git commit --patch 

показывает нам по одному hunk и спрашивает  Stage this hunk? Таким образом можно разбить изменения на разные коммиты

git status

покажет нам какие изменения еще остались, которые можно посмотреть с помощью git diff


еще есть  git add --interactive 

пятница, 10 мая 2024 г.

Poisson distribution

import matplotlib.pyplot as plt
from math import factorial, exp
from scipy.stats import poisson
 

lmbd = 3 # Y∼Pois(3)
r = 20 # range

distr = []
# за период произойдет ровно k событий
for k in range(r):
    # рассчитаем вероятности по формуле распределения Пуассона:
    p = poisson.pmf(k, lmbd)  # p = (lmbd ** k) / (factorial(k) * exp(lmbd))
    distr.append(p)


plt.plot(range(r), distr, 'bo')
plt.xticks(range(r))
plt.show()
Функция распределения для распределения Пуассона, то есть суммарную вероятность значений от 0 до заданного
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import poisson


lmbd = 3
r = 20
distr = []
# для значений от 0 до 19
for x in range(r):
    # найдём кумулятивные вероятности
    d = poisson.cdf(x, lmbd)
    distr.append(d)


plt.plot(range(r), distr, 'bo')
plt.xticks(range(r))
plt.show()

четверг, 9 мая 2024 г.

Normal distribution - cdf and ppf

from scipy import stats as st

# математическое ожидание
mu = 1000
# стандартное отклонение
sigma = 100

# задаём нормальное распределение с математическим ожиданием 1000 
# и стандартным отклонением 100
distr = st.norm(mu, sigma) 

# значение, для которого хотим найти вероятность
x = 1000

# считаем вероятность получить значение, равное x или меньше 
result = distr.cdf(x)  # обратная функция - ppf() (percent point function) позволяет по вероятности найти значение

print(result)  # выведет 0.5

Binomial distribution

from matplotlib import pyplot as plt
from math import factorial
from scipy.stats import binom

n = 30 # количество попыток
p = 0.07 # вероятность успеха

distr = []

#for k in range(0, n + 1):
#    choose = factorial(n) / (factorial(k) * factorial(n - k))
#    prob = choose * p**k * (1 - p)**(n - k) 
#    distr.append(prob)

# probability mass function — функция для дискретных распределений, показывает вероятность каждого значения
for k in range(0, n + 1):
    current_value = binom.pmf(k, n, p)
    distr.append(current_value)

plt.bar(range(0, n + 1), distr)

Вычисление процентилей, дисперсии и стандартного отклонения

import pandas as pd
from scipy import stats

x = pd.Series([3, 1, 2, 5, 6, 0, 10])

iqr_value = stats.iqr(x) # межквартильный размах
print(iqr_value)

import pandas as pd
import numpy as np

x = pd.Series([3, 1, 2, 5, 6, 0, 10])

data_percentile = np.percentile(x, 10) # 10-й процентиль
print(data_percentile)
При вычислении в NumPy используется так называемая интерполяция. По умолчанию в NumPy используется линейная интерполяция (method='linear')
import pandas as pd
import matplotlib.pyplot as plt

times = pd.Series([11.2, 20.5, 22.35, 31.1, 32.05, 33.8, 41.2, 42.15, 43.5, 44.123, 
	51.1, 52.712, 53.053, 54.012, 55.2, 61.987, 62.123, 63.123, 64.12, 65.5678, 66.16, 
	71.051, 72.531, 73.121, 74.71, 75.1233, 76.51, 77.12, 81.005, 82.01, 83.5, 84.323, 
	85.1, 86.1, 87.1, 88.12, 91.56, 92.056, 93.651, 94.777, 95.102, 96.105, 97.503, 
	98.003, 99.00005]) # данные

ax = times.value_counts(ascending=True).plot.bar()  # столбчатая диаграмма не подходит для отображения непрерывных величин
plt.show()

times.hist(bins=4)
plt.show()

import pandas as pd

data = pd.Series([1, 2, 3, 4, 5, 6])

mean = data.mean()
var = 0
for i in data:
    var += (i - mean) * (i - mean)
var /= len(data)  # дисперсия 
std = var**0.5  # стандартное отклонение

print(std)

суббота, 4 мая 2024 г.

explain


EXPLAIN 
SELECT user_id, 
             SUM(revenue) AS user_revenue
FROM online_store.orders
GROUP BY user_id;

  • costs — стоимость операции, которая может варьироваться; 
  • rows — количество записей, которое операция может вернуть; 
  • width — размер одной записи в байтах.

EXPLAIN ANALYZE 
SELECT user_id, 
             SUM(revenue) AS user_revenue
FROM online_store.orders
GROUP BY user_id;


Planning Time и Execution Time с планируемое и реальное время выполнения запроса соответственно 
  • actual time — реальное время выполнения операции; 
  • rows — реальное количество записей, которое выдаст операция; 
  • loops — количество повторений этой операции.
https://postgrespro.ru/docs/postgrespro/12/sql-explain

LTV

Составляем профили

  • идентификатор пользователя
  • начальный момент времени (время появления пользователя) 

SELECT p.user_id,
       DATE_TRUNC('day', first_ts)::date AS start_cohort,
       COUNT(*) OVER (PARTITION BY DATE_TRUNC('day', first_ts)) AS cohort_size
FROM online_store.profiles p;

Собираем таблицу сессий


WITH user_profile AS (SELECT p.user_id,
                             DATE_TRUNC('day', first_ts)::date AS start_cohort,
                             COUNT(*) OVER (PARTITION BY DATE_TRUNC('day', first_ts)) AS cohort_size
                      FROM online_store.profiles p)
                      
SELECT EXTRACT(DAY FROM AGE(DATE_TRUNC('day', o.event_dt), up.start_cohort)) AS lifetime,
        o.event_dt AS order_date,
        start_cohort,
        cohort_size,
        revenue
        FROM user_profile up
        JOIN online_store.orders o ON up.user_id = o.user_id;

-- в результате остаются только записи тех user_id, которые совершили покупки (размер когорты уже посчитан)

Считаем LTV


WITH user_profile AS (SELECT p.user_id,
                             DATE_TRUNC('day', first_ts)::date AS start_cohort,
                             COUNT(*) OVER (PARTITION BY DATE_TRUNC('day', first_ts)) AS cohort_size
                      FROM online_store.profiles p),
         sessions AS (SELECT EXTRACT(DAY FROM AGE(DATE_TRUNC('day', o.event_dt), up.start_cohort)) AS lifetime,
                             o.event_dt AS order_date,
                             start_cohort,
                             cohort_size,
                             revenue
                      FROM user_profile up
                      JOIN online_store.orders o ON up.user_id = o.user_id)

SELECT lifetime,
       start_cohort,
       SUM(revenue) OVER (PARTITION BY start_cohort ORDER BY lifetime) / cohort_size AS ltv
FROM sessions; 

--запрос порождает большое количество дублей т.к. мы избавились от order_date

WITH user_profile AS (SELECT p.user_id,
                             DATE_TRUNC('day', first_ts)::date AS start_cohort,
                             COUNT(*) OVER (PARTITION BY DATE_TRUNC('day', first_ts)) AS cohort_size
                      FROM online_store.profiles p),
         sessions AS (SELECT EXTRACT(DAY FROM AGE(DATE_TRUNC('day', o.event_dt), up.start_cohort)) AS lifetime,
                             o.event_dt AS order_date,
                             start_cohort,
                             cohort_size,
                             revenue
                      FROM user_profile up
                      JOIN online_store.orders o ON up.user_id = o.user_id),
          ltv_raw AS (SELECT lifetime,
                             start_cohort,
                             SUM(revenue) OVER (PARTITION BY start_cohort ORDER BY lifetime) / cohort_size AS ltv
                      FROM sessions)
SELECT lifetime,
       start_cohort,
       ltv
FROM ltv_raw
GROUP BY lifetime, start_cohort, ltv
ORDER BY start_cohort, lifetime;
Другой пример:

WITH user_profile AS (
    SELECT u.user_id,
           CAST(DATE_TRUNC('month', u.created_at) AS date) as start_cohort,
           COUNT(u.user_id) OVER(PARTITION BY CAST(DATE_TRUNC('month', u.created_at) AS date)) AS cohort_size
    FROM tools_shop.users AS u
),

 
sessions AS ( 
    SELECT EXTRACT(MONTH FROM AGE(
               DATE_TRUNC('day', o.created_at), 
               p.start_cohort
           )) AS lifetime,
            CAST( DATE_TRUNC('month', o.created_at) AS date) order_date, --FUCK
            p.start_cohort,
            p.cohort_size,
            o.total_amt
    FROM user_profile AS p
    JOIN tools_shop.orders AS o ON p.user_id = o.user_id
),

ltv_raw AS (
    SELECT lifetime,
       start_cohort,
       cohort_size,
       SUM(total_amt) OVER (PARTITION BY start_cohort ORDER BY lifetime) / cohort_size AS ltv
FROM sessions
)

SELECT lifetime,
       start_cohort,
       ltv
FROM ltv_raw
WHERE EXTRACT(YEAR FROM start_cohort) = 2019
GROUP BY lifetime, start_cohort, ltv
ORDER BY start_cohort, lifetime;

Some Agile

Обзор Agile. Это методология, метод или философия? 

 


Churn

WITH profile AS (SELECT user_id,
                         dt AS cohort
                  FROM online_store.profiles
                  WHERE channel = 'Organic'),
     pr_session AS (SELECT user_id,
                           session_start::date AS cnt_date
                    FROM online_store.sessions),
     cohorts AS (SELECT cohort,
                        cnt_date,
                        COUNT(DISTINCT profile.user_id) AS cnt_user
                        FROM profile JOIN pr_session ON profile.user_id = pr_session.user_id
                        GROUP BY cohort, cnt_date)
                        
SELECT *,     
       LAG(cnt_user) OVER (PARTITION BY cohort ORDER BY cnt_date) AS previous_day_users_cnt,
       ROUND((1 - (cnt_user::numeric/ LAG(cnt_user) OVER (PARTITION BY cohort ORDER BY cnt_date))) * 100, 2) AS churn_rate
FROM cohorts;
Другой пример:
WITH
profiles AS (
    -- пользователи совершившие покупку, дата первого события
    SELECT DISTINCT u.user_id,
                    CAST(DATE_TRUNC('month', MIN(e.event_time)) AS date) AS dt
    FROM tools_shop.users AS u
    INNER JOIN tools_shop.orders AS o ON u.user_id=o.user_id
    LEFT JOIN tools_shop.events AS e ON u.user_id=e.user_id
    GROUP BY u.user_id
),

cohorts AS (
    SELECT p.dt as cohort,
           CAST(DATE_TRUNC('month', e.event_time) AS date) AS event_month,
           CAST(COUNT(DISTINCT e.user_id) AS numeric) AS users
    FROM profiles AS p
    LEFT JOIN tools_shop.events AS e ON p.user_id=e.user_id
    GROUP BY p.dt, event_month
)


SELECT cohorts.cohort,
       cohorts.event_month,
       cohorts.users,
       LAG(cohorts.users) OVER(PARTITION BY cohorts.cohort ORDER BY cohorts.event_month) AS prev_month_users,
       ROUND(
           (1 - cohorts.users / LAG(cohorts.users) OVER(PARTITION BY cohorts.cohort ORDER BY cohorts.event_month)) *100
       , 2)
FROM cohorts;

пятница, 3 мая 2024 г.

Retention

Допустим мы имеем таблицу с профилями пользователей. Профилем называется таблица, где каждому пользователю соответствует метка, которая указывает, к какой когорте относится пользователь.
Профили могут быть получены из журнала посещений. Для каждого идентификатора пользователя нужно назначить когорту, дату первого посещения сайта.
WITH profile AS (SELECT user_id,
                         dt AS cohort
                  FROM online_store.profiles
                  WHERE channel = 'Organic'),
     pr_session AS (SELECT user_id,
                           session_start::date AS session_date
                    FROM online_store.sessions)
SELECT *
FROM profile JOIN pr_session ON profile.user_id = pr_session.user_id;
Определяем количество пользователей на каждую дату и на каждую когорту
WITH profile AS (SELECT user_id,
                         dt AS cohort
                  FROM online_store.profiles
                  WHERE channel = 'Organic'),
     pr_session AS (SELECT user_id,
                           session_start::date AS session_date
                    FROM online_store.sessions)
SELECT cohort,
       session_date,
       COUNT(DISTINCT pr_session.user_id) AS cnt_user
FROM profile JOIN pr_session ON profile.user_id = pr_session.user_id
GROUP BY cohort, session_date;

WITH profile AS (SELECT user_id,
                         dt AS cohort
                  FROM online_store.profiles
                  WHERE channel = 'Organic'),
     pr_session AS (SELECT user_id,
                           session_start::date AS cnt_date
                    FROM online_store.sessions),
     cohorts AS (SELECT cohort,
                        cnt_date,
                        COUNT(DISTINCT profile.user_id) AS cnt_user
                        FROM profile JOIN pr_session ON profile.user_id = pr_session.user_id
                        GROUP BY cohort, cnt_date)
SELECT *,
       MAX(cnt_user) OVER (PARTITION BY cohort) AS cnt_user_in_cohort,
       ROUND(cnt_user::numeric/MAX(cnt_user) OVER (PARTITION BY cohort)::numeric,3)
FROM cohorts;
Другой пример:
Первым событием возьмем первую покупку

WITH
first_ts AS (
    SELECT o.user_id,
           o.paid_at,
           MIN(o.paid_at) OVER(PARTITION BY o.user_id) AS first_dt
    FROM tools_shop.orders AS o
),

cohorts AS (
    SELECT first_ts.user_id,
           CAST(DATE_TRUNC('month', first_ts.paid_at) AS date) AS paid_at,
           CAST(DATE_TRUNC('month', first_ts.first_dt) AS date) AS cohort
    FROM first_ts
    WHERE EXTRACT(YEAR FROM first_ts.first_dt) = 2020
),

users AS (
    SELECT cohorts.cohort,
           cohorts.paid_at,
           CAST(COUNT(DISTINCT cohorts.user_id) AS numeric) AS users
    FROM cohorts
    GROUP BY cohorts.cohort, cohorts.paid_at
)

SELECT users.cohort,
       users.paid_at,
       ROUND(users.users / MAX(users.users) OVER(PARTITION BY users.cohort), 4)*100 AS retention
FROM users;

четверг, 2 мая 2024 г.

Find out how long it takes for your customers to convert

 https://support.google.com/google-ads/answer/6239119?hl=en

conversions can be reported up to 90 days after the click, depending on the conversion window you’ve chosen.