ORACLE one hot encoding:对所有可用列使用 PIVOT 运算符

Posted

技术标签:

【中文标题】ORACLE one hot encoding:对所有可用列使用 PIVOT 运算符【英文标题】:ORACLE one hot encoding: Use PIVOT operator for all available columns 【发布时间】:2020-09-04 15:05:55 【问题描述】:

假设我有一个名为 t1 的表:

CLID PRODUCT
1    A
1    B
2    A
2    C
3    A
3    C

我需要在“产品”字段上进行某种单热编码。 在 Oracle 中,我们至少可以这样做:

select * from(
    select clid, product 
    from t1
    pivot(
        count(product)
        for product 
        in('A', 'B', 'C')
    )
)

然后我们得到结果:

CLID A B C
1    1 1 0
2    1 0 1 
3    1 0 1

但是当我们有大量产品(假设 1000 件)时就会出现问题,在这种情况下,将所有产品都置于 IN 状态会非常不方便。

所以我的问题是,是否有任何方法可以避免将所有可能的值放入“IN”? 如果没有这样的选项,那么也许有另一种方法可以在 Oracle sql(或 pl/sql?)中进行 one-hot-encoding?

【问题讨论】:

Oracle 可能不支持足够的列来支持您的查询。 您的最终结果应该是包含所有这些列的表,还是每个 CLID 的单个值('110'、'101'、'101'),还是其他?是否有单独的产品列表,可能在另一个表中? 在最终结果中,我需要 CLID 列中的值是唯一的(不是 1,1,2,3,而是 1,2,3)我还需要创建名为产品值的附加列(原始PRODUCT 字段需要删除)。在我的示例中,您可以看到我们获得了 3 个新列(A、B 和 C),并且这些列中的值仅为 1 或 0(1 代表产品的存在(列的名称表示我们正是哪个产品)对于某些 CLID值,0 代表不存在)。不,就我而言,我没有另一个包含产品列表的表格,但我可以创建它 什么会消耗这些数据?它对人类来说似乎不是很有用;但如果稍后将在其他地方处理它,那么您可以将其生成为 XML - 您可以使用 in() 子句中的查询进行 XML 透视。否则,这可能是报告层应该做的事情。 嗯,实际上你是 100% 正确的,对人类来说它不是很有用:) 但是我需要完成这个过程才能将检索到的数据放入机器学习模型中,因为它们只适用于独特的案例数据。因此,为了捕获客户产品,我们需要为每个产品创建值为 1 或 0 的列。 【参考方案1】:

无论您的表中有多少不同的产品,您都可以使用 PL/SQL 匿名块自动构建查询,但您需要了解,您不能将超过 1000 个值放入数据透视子句中,因为您不能超过 1000 列。

我会做这样的事情(假设总是少于 1000 个值)

测试用例(创建表格和测试值)

SQL> create table t ( CLID number , PRODUCT varchar2(10) )  ;

Table created.

SQL> insert into t ( clid , product )
  2  with x ( a , b ) as
(
select 1  ,  'A' from dual union all
select 1  ,  'B' from dual union all
select 2  ,  'A' from dual union all
select 2  ,  'C' from dual union all
select 3  ,  'A' from dual union all
select 3  ,  'C' from dual union all
select 4  ,  'D' from dual union all
select 4  ,  'E' from dual union all
select 5  ,  'B' from dual union all
select 5  ,  'C' from dual union all
select 5  ,  'D' from dual union all
select 5  ,  'E' from dual
)
select a , b from x ;

12 rows created.

SQL> commit ;

Commit complete.

PLSQL 构造

然后,无论有多少不同的产品,自动获取查询

set serveroutput on size unlimited lines 220 pages 0
declare
v_query       clob;
out_string    varchar2(100);
cursor c_ids 
is 
select distinct product, count(distinct(product)) over () tot_rows from t order by 1 asc;
procedure print_clob_to_output (p_clob in clob)
    is
      l_offset     pls_integer := 1;
      l_chars      pls_integer;
    begin
        loop
            exit when l_offset > dbms_lob.getlength(p_clob);
            l_chars := dbms_lob.instr(p_clob, chr(10), l_offset, 1);
            if l_chars is null or l_chars = 0 then
                l_chars := dbms_lob.getlength(p_clob) + 1;
            end if;
            dbms_output.put_line(dbms_lob.substr(p_clob, l_chars - l_offset, l_offset));
            l_offset := l_chars + 1;
        end loop;
    end print_clob_to_output;
begin
    dbms_output.enable(null);
    for item in c_ids 
    loop
        if item.tot_rows >= 1000
        then 
            raise_application_error(-20001,'Maximum number of 1000 columns are not allowed',true);
        end if;
        out_string := item.product;
        if c_ids%rowcount = 1 
        then 
            v_query := 'select * from (';
            dbms_lob.append(v_query,''||chr(10)||'');
            dbms_lob.append(v_query,'  select *  ');
            dbms_lob.append(v_query,''||chr(10)||'');
            dbms_lob.append(v_query,' from t '); 
            dbms_lob.append(v_query,''||chr(10)||'');
            dbms_lob.append(v_query,' pivot( '); 
            dbms_lob.append(v_query,''||chr(10)||'');
            dbms_lob.append(v_query,' count(product) '); 
            dbms_lob.append(v_query,''||chr(10)||'');
            dbms_lob.append(v_query,' for product in ( '''||out_string||''' , ');
        elsif c_ids%rowcount < item.tot_rows then
            dbms_lob.append(v_query,''||chr(10)||'');
            dbms_lob.append(v_query,' '''||out_string||''' ,');
        else 
            dbms_lob.append(v_query,''||chr(10)||'');
            dbms_lob.append(v_query,' '''||out_string||''' ) ');
        end if;
    end loop;
    dbms_lob.append(v_query,''||chr(10)||'');
    dbms_lob.append(v_query,' ) )');
    print_clob_to_output(v_query);
end;
/

执行

SQL> @query.sql
SQL> set serveroutput on size unlimited lines 220 pages 0
SQL>     declare
  2      v_query       clob;
  3      out_string    varchar2(100);
  4      cursor c_ids
  5      is
  6      select distinct product, count(distinct(product)) over () tot_rows from t order by 1 asc;
  7      procedure print_clob_to_output (p_clob in clob)
  8          is
  9            l_offset     pls_integer := 1;
 10            l_chars      pls_integer;
 11          begin
 12              loop
 13                  exit when l_offset > dbms_lob.getlength(p_clob);
 14                  l_chars := dbms_lob.instr(p_clob, chr(10), l_offset, 1);
 15                  if l_chars is null or l_chars = 0 then
 16                      l_chars := dbms_lob.getlength(p_clob) + 1;
 17                  end if;
 18                  dbms_output.put_line(dbms_lob.substr(p_clob, l_chars - l_offset, l_offset));
 19                  l_offset := l_chars + 1;
 20              end loop;
 21          end print_clob_to_output;
 22      begin
 23          dbms_output.enable(null);
 24          for item in c_ids
 25          loop
 26              if item.tot_rows >= 1000
 27              then
 28                  raise_application_error(-20001,'Maximum number of 1000 columns are not allowed',true);
 29              end if;
 30              out_string := item.product;
 31              if c_ids%rowcount = 1
 32              then
 33                  v_query := 'select * from (';
 34                  dbms_lob.append(v_query,''||chr(10)||'');
 35                  dbms_lob.append(v_query,'  select *  ');
 36                  dbms_lob.append(v_query,''||chr(10)||'');
 37                  dbms_lob.append(v_query,' from t ');
 38                                  dbms_lob.append(v_query,''||chr(10)||'');
 39                  dbms_lob.append(v_query,' pivot( ');
 40                                  dbms_lob.append(v_query,''||chr(10)||'');
 41                  dbms_lob.append(v_query,' count(product) ');
 42                                  dbms_lob.append(v_query,''||chr(10)||'');
 43                  dbms_lob.append(v_query,' for product in ( '''||out_string||''' , ');
 44                          elsif c_ids%rowcount < item.tot_rows then
 45                  dbms_lob.append(v_query,''||chr(10)||'');
 46                  dbms_lob.append(v_query,' '''||out_string||''' ,');
 47              else
 48                  dbms_lob.append(v_query,''||chr(10)||'');
 49                  dbms_lob.append(v_query,' '''||out_string||''' ) ');
 50              end if;
 51          end loop;
 52          dbms_lob.append(v_query,''||chr(10)||'');
 53          dbms_lob.append(v_query,' ) )');
 54          print_clob_to_output(v_query);
 55      end;
 56      /
select * from (
select *
from t
pivot(
count(product)
for product in ( 'A' ,
'B' ,
'C' ,
'D' ,
'E' )
) )

PL/SQL procedure successfully completed.

SQL> select * from (
select *
from t
pivot(
count(product)
for product in ( 'A' ,
'B' ,
'C' ,
'D' ,
'E' )
) ) ;

      CLID        'A'        'B'        'C'        'D'        'E'
---------- ---------- ---------- ---------- ---------- ----------
         1          1          1          0          0          0
         2          1          0          1          0          0
         4          0          0          0          1          1
         5          0          1          1          1          1
         3          1          0          1          0          0

【讨论】:

以上是关于ORACLE one hot encoding:对所有可用列使用 PIVOT 运算符的主要内容,如果未能解决你的问题,请参考以下文章

如何从包含集合的 pandas 列转置和转换为“one-hot-encode”样式?

R语言使用caret包的dummyVars函数自动对训练数据集中的因子(factor)变量进行独热编码(one hot encoding)

数据处理——One-Hot Encoding

从 scikit-learn 中的 one-hot-encoding 回溯分类特征?

Scikit-Learn One-hot-encode 在训练/测试拆分之前或之后

one-hot encoding是啥意思