Postgres: считать ряды с левым соединением - PullRequest
0 голосов
/ 01 октября 2018

Я пытаюсь провести некоторую аналитику с использованием Postgres, где у меня есть 2 таблицы, которые называются: predictionstate и pageviews.

Таблица predictionstate:

Эта таблица содержит столбцы с результатами нашего алгоритма, используя следующую структуру:

  • id ({company_identifier}:{user_identifier})
  • модель (значение строки ссылки)
  • прогноз (число с плавающей точкой от 0,0 до 1,0)

Таблица pageviews:

Эта таблица содержит информацию о пользователе, используя следующую структуру:

  • company_identifier
  • user_identifier
  • pageview_current_url_type

Вопрос

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

WITH ranges AS (
  SELECT
    myrange::text || '-' || (myrange + 0.1)::text AS segment,
    myrange as r_min, myrange + 0.1 as r_max
  FROM generate_series(0.0, 0.9, 0.1) AS myrange
)
SELECT
  SPLIT_PART(p.id, ':', 1) as company_identifier,
  p.model,
  r.segment,
  COUNT(DISTINCT(SPLIT_PART(p.id, ':', 2))) as "segment_users"
FROM
  ranges r
INNER JOIN predictionstate p ON p.prediction BETWEEN r.r_min AND r.r_max
GROUP BY company_identifier, p.model, r.segment
ORDER BY company_identifier, p.model, r.segment;

Но проблема, которая у меня возникла, так как я не знаю точно, как это сделать, это то, что для каждого (компании, модели, сегмента), и нужно получитьданные о его точности, запрос к таблице pageviews и определение pageview_current_url_type == 'BUYSUCCESS'.

То, что я пробовал, но не сработало:

WITH ranges AS (
  SELECT
    myrange::text || '-' || (myrange + 0.1)::text AS segment,
    myrange as r_min, myrange + 0.1 as r_max
  FROM generate_series(0.0, 0.9, 0.1) AS myrange
)
SELECT
  SPLIT_PART(p.id, ':', 1) as company_identifier,
  p.model,
  r.segment,
  COUNT(DISTINCT(SPLIT_PART(p.id, ':', 2))) as "segment_users",
  b.n as "converted_users"
FROM
  ranges r,
  (
    SELECT COUNT(DISTINCT(pvs.user_identifier)) as n
    FROM pageviews pvs
    INNER JOIN (
        SELECT
            SPLIT_PART(id, ':', 1) as company_identifier,
            SPLIT_PART(id, ':', 2) as user_identifier
        FROM predictionstate ps
        WHERE prediction BETWEEN r.r_min AND r.r_max ) users
        ON (
            pvs.user_identifier = users.user_identifier AND
            pvs.company_identifier= users.company_identifier) 
        WHERE pageview_current_url_type = 'BUYSUCCESS'

  ) b
INNER JOIN predictionstate p ON p.prediction BETWEEN r.r_min AND r.r_max
GROUP BY company_identifier, p.model, r.segment
ORDER BY company_identifier, p.model, r.segment;

TL; DR:Мне нужно подсчитать JOIN на основе основных пользователей запроса.

РЕДАКТИРОВАТЬ:

Я добавил SQL Fiddle https://www.db -fiddle.com / f/ 5sQiZD6mHwdnwvVfvL9MAh / 0 .

Что я хочу знать, для тех segment_users, сколько из них имеет pageview_current_url_type = 'BUYSUCCESS', добавить еще один столбец к результату: segmented_really_bought.

РЕДАКТИРОВАТЬ 2: Еще одна попытка не работает (ОШИБКА: столбец "p.id" должен появляться в предложении GROUP BY или использоваться в статистической функции)

WITH ranges AS (
  SELECT
    myrange::text || '-' || (myrange + 0.1)::text AS segment,
    myrange as r_min, myrange + 0.1 as r_max
  FROM generate_series(0.0, 0.9, 0.1) AS myrange
)
SELECT
  SPLIT_PART(p.id, ':', 1) as company_identifier,
  p.model,
  r.segment,
  COUNT(DISTINCT(SPLIT_PART(p.id, ':', 2))) as "segment_users",
  COUNT(b.*) as "converted_users"
FROM
  ranges r
INNER JOIN predictionstate p ON p.prediction BETWEEN r.r_min AND r.r_max
INNER JOIN (
  SELECT users.company_identifier, COUNT(users.user_identifier) AS n
  FROM pageviews
  INNER JOIN (
    SELECT SPLIT_PART(ps.id, ':', 2) AS user_identifier,
           SPLIT_PART(ps.id, ':', 1) AS company_identifier
    FROM predictionstate ps
    WHERE provider_id=47 AND
          prediction > 0.7
   ) users ON (
      pageviews.user_identifier=users.user_identifier AND
      pageviews.company_identifier=users.company_identifier
    )
  WHERE pageview_current_url_type='BUYSUCCESS'
  GROUP BY users.company_identifier
) AS b
ON (
  b.company_identifier = company_identifier
)
GROUP BY company_identifier, p.model, r.segment
ORDER BY company_identifier, p.model, r.segment;

РЕДАКТИРОВАТЬ 3: Добавлен желаемый вывод

Генерируется с использованием этого кода: https://gist.github.com/brunoalano/479265b934a67dc02092fb54a846fe1e

company, model, segment, segment_users, really_bought
company_a, model_a, 0.3-0.4, 1, 3
company_a, model_a, 0.5-0.6, 1, 1
company_a, model_b, 0.2-0.3, 1, 3
company_a, model_c, 0.2-0.3, 1, 1
company_a, model_c, 0.7-0.8, 1, 3
company_b, model_a, 0.3-0.4, 3, 2
company_b, model_b, 0.5-0.6, 2, 1
company_b, model_b, 0.6-0.7, 1, 1
company_b, model_c, 0.5-0.6, 1, 0
company_b, model_c, 0.8-0.9, 1, 1

Ответы [ 2 ]

0 голосов
/ 03 октября 2018

demo: db <> fiddle

WITH ranges AS (
  SELECT
    myrange::text || '-' || (myrange + 0.1)::text AS segment,
    myrange as r_min, myrange + 0.1 as r_max
  FROM generate_series(0.0, 0.9, 0.1) AS myrange
), pstate AS (                                         -- A
  SELECT 
    SPLIT_PART(ps.id, ':', 1) AS company_identifier,
    SPLIT_PART(ps.id, ':', 2) AS user_identifier,
    model,
    prediction
  FROM predictionstate ps
)
SELECT 
  company_identifier, model, segment,
  COUNT(DISTINCT user_identifier) as segment_users,    -- B
  -- C: 
  COUNT(user_identifier) FILTER (WHERE pageview_current_url_type = 'BUYSUCCESS') as really_bought
FROM pstate ps
LEFT JOIN ranges r 
ON prediction BETWEEN r_min AND r_max
LEFT JOIN pageviews pv 
USING (company_identifier, user_identifier)
GROUP BY company_identifier, model, segment
ORDER BY company_identifier, model, segment

A: Я бы порекомендовал разделить ваш столбец id на два столбца для лучшей обработки.Это сэкономит вам много времени на разбиение строки (на написание запросов и их выполнение), и это станет более читабельным.Вот почему я добавил второй CTE.

B: COUNT(DISTINCT) подсчитывает отдельных пользователей в группе

C: подсчитывает всех пользователей (не различаются), но отфильтровывает ожидаемый статус перед подсчетом.


Мне было интересно: а что если прогноз точно на пороге, например 0.3.В предложении BETWEEN этот диапазон будет объединен как в диапазоне 0.2-0.3, так и в диапазоне 0.3-0.4 (поскольку BETWEEN равно r_min >= x >= r_max).Было бы лучше определить диапазоны как r_min >= x > r_max или r_min > x >= r_max.Я сделал соединение, как вы упомянули в своем примере, но я бы предпочел изменить его.Я до сих пор не знаю, в каком направлении

0 голосов
/ 03 октября 2018

Трудно сказать без примера вывода, что вам нужно, но я думаю, что вы ищете:

WITH ranges AS (
  SELECT
    myrange::text || '-' || (myrange + 0.1)::text AS segment,
    myrange as r_min, myrange + 0.1 as r_max
  FROM generate_series(0.0, 0.9, 0.1) AS myrange
)
SELECT
  p.company_identifier,
  p.model,
  r.segment,
  COUNT(DISTINCT(p.user_identifier)) as "segment_users",
  COUNT(CASE WHEN pv.pageview_current_url_type = 'BUYSUCCESS' THEN 1 END) AS segmented_really_bought
FROM
  ranges r
INNER JOIN (
  SELECT
    SPLIT_PART(id, ':', 1) as company_identifier,
    SPLIT_PART(id, ':', 2) as user_identifier,
    model,
    prediction
  FROM
    predictionstate
  ) p ON p.prediction BETWEEN r.r_min AND r.r_max
LEFT JOIN pageviews pv ON 
  p.company_identifier = pv.company_identifier
  AND p.user_identifier = pv.user_identifier
GROUP BY p.company_identifier, p.model, r.segment
ORDER BY p.company_identifier, p.model, r.segment;

Изменения в вашем скриптовом запросе:

  • замененоpredictionstate с подзапросом, к которому мы присоединяемся, где мы используем логику split_part, чтобы получить идентификаторы компании и пользователя в виде отдельных столбцов
  • использовал эти идентификаторы для LEFT JOIN до pageviews
  • добавлен segmented_really_bought столбец с CASEd COUNT
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...