Представление категориального столбца как One-Hot Encoding с использованием SQL

Я хочу представить строковый столбец как двоичную цифру 1 или 0, повернув строковый столбец и сделав его значения в качестве заголовка с помощью SQL (Snowflake). Это будет эквивалент Python pd.get_dummies, где функция преобразует категориальный столбец в столбцы с горячим кодированием. Как мне это сделать?

Пример данных:

идентификатор ценить А 'G802' А 'Р620' А '' Б 'J209' Б 'B009' С '' С 'Р509'

Ожидаемый результат:

идентификатор G802 620 рэндов J209 B009 509 рэндов А 1 1 0 0 0 Б 0 0 1 1 0 С 0 0 0 0 1

Мне нужно, чтобы запрос был динамическим, поскольку в столбце value есть больше случайных значений. Порядок столбца может быть любым. Значение '' в value может или не может быть частью сводной таблицы. Я попробовал несколько вариантов с функцией pivot, но безуспешно. Я очень ценю вашу помощь. Спасибо!

Snowflake поддерживает динамическое поворот, на что стоит обратить внимание: docs.snowflake.com/en/sql-reference/constructs/… . Однако у него есть странное поведение: имена сгенерированных столбцов заключаются в одинарные кавычки.

Error_2646 28.08.2024 18:01
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
1
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы не найдете способа сделать SQL таким динамическим.

Лично у меня есть собственная библиотека шаблонов, которая отображает SQL-код, и я использую Jinja для написания своих шаблонов.

rasgoql делает то же самое, и вот джинджа для горячего кодирования:

SELECT *,
{% for val in distinct_col_vals %}
    {%- if val is not none %}
    CASE WHEN {{ column }} = {{ "'" ~ val ~ "'"}} THEN 1 ELSE 0 END as {{ column ~ '_' ~ val }}{{ ', ' if not loop.last else '' }}
    {%- else %}
    CASE WHEN {{ column }} IS NULL THEN 1 ELSE 0 END as {{ column }}_IS_NULL{{ ', ' if not loop.last else '' }}
    {%- endif -%}
{% endfor %}
FROM {{ source_table }}

Очевидно, вам нужно передать отдельные значения в качестве аргумента, что требует, чтобы вы либо сделали это в Python, либо создали себе функцию run_query(), которая делает это автоматически, что я и делаю.

Без особых усилий вы можете получить шаблон, который автоматически извлекает значения:

{%- set run_query_error_message -%}
This transform depends on dynamic values to work, but no Data Warehouse connection is available. 
Instead, please use the `list_of_vals` argument to provide these values explicitly
{%- endset -%}

{%- if list_of_vals is not defined -%}
    {%- set results = run_query("SELECT DISTINCT " +  column + " FROM " + source_table) -%}
    {%- if results is none -%}
        {{ raise_exception(run_query_error_message) }}
    {%- endif -%}
    {%- set distinct_col_vals = results[column].to_list() -%}
{%- else -%}
    {%- set distinct_col_vals = list_of_vals -%}
{%- endif -%}

SELECT *,
{% for val in distinct_col_vals %}
    {%- if val is not none %}
    CASE WHEN {{ column }} = {{ "'" ~ val ~ "'"}} THEN 1 ELSE 0 END as {{ column ~ '_' ~ val }}{{ ', ' if not loop.last else '' }}
    {%- else %}
    CASE WHEN {{ column }} IS NULL THEN 1 ELSE 0 END as {{ column }}_IS_NULL{{ ', ' if not loop.last else '' }}
    {%- endif -%}
{% endfor %}
FROM {{ source_table }}

Вы можете подумать, что это перебор. И да, это так. Но я много пишу динамический SQL, и, по моему мнению, оно того стоит.

Но краткий ответ на ваш вопрос: нет, SQL требует, чтобы вы вводили столбцы, и вы не сделаете сам движок динамическим. Так что либо смиритесь с этим, изучите приемы динамического написания SQL для вас с использованием Excel или Python, либо переборщите и создайте библиотеку шаблонов, такую ​​​​как rasgoql.

"You're not going to find a way to make SQL dynamic like that." — SQL — выразительный язык. Настолько недооценено. Вот подход на основе чистого SQL: stackoverflow.com/a/78924375/5070879
Lukasz Szozda 28.08.2024 18:37

Спасибо, Джош. Это решение выглядит очень интересным и мне очень знакомо. Дает мне что-то, что я могу изучить больше.

Krishnang K Dalal 28.08.2024 18:56

Как указывали другие: Snowflake, кажется, поддерживает способ выполнения вашей конкретной задачи, но не все СУБД будут поддерживать это - и то, что Snowflake предлагает динамический PIVOT, не означает, что вы не столкнетесь с подобной проблемой в будущем, поэтому я хотел чтобы дать вам питонический способ сделать SQL динамическим, не полагаясь на функции, отличные от ANSI.

Josh 28.08.2024 20:35
Ответ принят как подходящий

Этого можно добиться с помощью SQL и динамического PIVOT:

SELECT *
FROM (SELECT id, VALUE, IFF(value='',0, 1) AS VALUE2 FROM t)
PIVOT (MAX(VALUE2) FOR VALUE IN (SELECT VALUE FROM t WHERE VALUE != '') 
       DEFAULT ON NULL (0));

Для тестовых данных:

CREATE OR REPLACE TABLE t(id TEXT, VALUE TEXT) AS
SELECT 'A', 'G802' UNION ALL 
SELECT 'A', 'R620' UNION ALL 
SELECT 'A', '' UNION ALL 
SELECT 'B', 'J209'UNION ALL  
SELECT 'B', 'B009'UNION ALL  
SELECT 'C', '' UNION ALL 
SELECT 'C', 'R509';

Выход:

Как это работает:

  • IFF(value='',0, 1) AS VALUE2 вспомогательный столбец для категоризации данных 0 или 1
  • PIVOT MAX(VALUE2) — результат сводной таблицы на основе вспомогательного столбца
  • VALUE IN (SELECT VALUE FROM t WHERE VALUE != '') - динамический поворот, пропуск ''
  • если не заморачиваться с пустой колонкой '', то просто VALUE IN (ANY)

Используя VALUE IN (ANY) и EXCLUDE:

SELECT * EXCLUDE "''"
FROM (SELECT id, VALUE, IFF(value='',0, 1) AS VALUE2 FROM t)
PIVOT (MAX(VALUE2) FOR VALUE IN (ANY) DEFAULT ON NULL (0));

красивый! большое спасибо. В итоге я разработал аналогичное решение, но ваше гораздо более краткое. Принимая ваш ответ. Спасибо!

Krishnang K Dalal 28.08.2024 18:54

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

Krishnang K Dalal 28.08.2024 21:12

Вам необходимо явно использовать псевдонимы столбцов (теряя динамический аспект). На сегодняшний день в SQL Pivot нет переключателя, позволяющего изменить это поведение.

Lukasz Szozda 28.08.2024 22:27

Другие вопросы по теме