본문 바로가기
통계프로그램 비교 시리즈/오라클함수 비교(R & Python)

COUNT 오라클 함수 [Oracle, Pandas, R Prog, Dplyr, Sqldf, Pandasql, Data.Table, DuckDB]

by 기서무나구물 2021. 11. 22.

포스팅 목차

    * 파이썬 & R 패키지 호출 및 예제 데이터 생성 링크


    [ COUNT Oracle Function ]

     


    COUNT함수는 쿼리에 의해 반환된 행의 수를 반환한다. 집계 함수 또는 분석함수로 이용할 수 있다.

     

    • 함수설명 : COUNT 오라클 함수 링크
    • 157번 참조 Check whether all employees number are indeed unique.
    • 유사문제 : 97 / 103 / 104 / 132
    • Cross Join 생성(dummy) : 97 / 140
    • fuzzyjoin::fuzzy_left_join : 113 / 116

     


    1. Oracle(오라클)

    • 다음 예제는 COUNT 함수를 집계함수로 이용.

     

    1) 집계함수 예제

    다음 함수는 테이블의 건수를 집계하는 집계 함수 예제이다.

    Oracle Programming
    SELECT COUNT(*) "Total" FROM employees;
    
    SELECT COUNT(commission_pct) "Count" FROM employees;
    
    SELECT COUNT(DISTINCT manager_id) "Managers" FROM employees;
    
    SELECT COUNT(*) "Allstars" FROM employees WHERE commission_pct > 0;

     

    Results
    Total
    ------------
    14
    Count
    ------------
    4
    Managers
    ------------
    6
    Allstars
    ------------
    3

     


    2) 분석함수 예제

    개별 직원의 급여를 기준으로 일정 범위 내에 ( 직원 급여 - 50 ~ 직원 급여 + 150 ) 급여를 수령하는 직원들의 수를 집계한다.

     

    Oracle Programming
    SELECT ENAME, SAL,
           COUNT(*) OVER (ORDER BY SAL RANGE BETWEEN 50 PRECEDING AND
                          150 FOLLOWING) AS mov_count
    FROM   EMP
    ORDER  BY SAL, ENAME;

     

    Results
    ENAME	SAL	MOV_COUNT
    -------------------------
    SMITH	800	2
    JAMES	950	2
    ADAMS	1100	3
    MARTIN	1250	3
    WARD	1250	3
    MILLER	1300	3
    TURNER	1500	2
    ALLEN	1600	1
    CLARK	2450	1
    BLAKE	2850	4
    JONES	2975	3
    FORD	3000	3
    SCOTT	3000	3
    KING	5000	1

     


    2. Python Pandas(파이썬)

     

    1) 집계함수

     

    Python Programming
    #Total
    display( emp.agg({'empno':['count']}) )
    
    display( np.sum(emp['empno'].count()) )

     

    Results
    	empno
    count	14​

     

    Results
    14

     


     

    Python Programming
    # count
    display( emp['comm'].notnull().sum() )   # null을 제외한 값을 카운트
    display( (~emp['comm'].isna()).sum() )   # na값을 제외한 값을 카운트
    display( emp['comm'].notna().sum()   )   # na값을 제외한 값을 카운트

     

    Results
    4
    
    4
    
    4

     


     

    Python Programming
    # Managers
    display( emp.agg({'mgr':['nunique']}) )  # 중복을 제외한 값을 카운트
    display( emp['mgr'].nunique() )          # 중복을 제외한 값을 카운트

     

    Results
    	mgr
    nunique	6

     

    Results
     
    6

     


     

    Python Programming
    # Allstars
    
    emp[emp['comm']>0][['comm']].count()    # 커미션(comm) 값이 양수인 값을 카운트

     

    Results
    comm    3
    dtype: int64

     

     


    2) 분석함수

    Python Programming
    withmooc = emp.copy()
    
    # 현재 sal의 값을 기준으로 x-50 ~ x+150 사이의 값을 카운트
    withmooc['range_cnt'] = withmooc['sal'].apply(lambda x: withmooc['sal'].between(x-50, x + 150, inclusive=True).sum())
    
    withmooc.sort_values(by = 'sal').head(10)

     

    Results
    	empno	ename	job		mgr	hiredate	sal	comm	deptno	range_cnt
    0	7369	SMITH	CLERK		7902.0	1980/12/17	800	NaN	20	2
    11	7900	JAMES	CLERK		7698.0	1981/12/03	950	NaN	30	2
    10	7876	ADAMS	CLERK		7788.0	1983/01/12	1100	NaN	20	3
    2	7521	WARD	SALESMAN	7698.0	1981/02/22	1250	500.0	30	3
    4	7654	MARTIN	SALESMAN	7698.0	1981/09/28	1250	1400.0	30	3
    13	7934	MILLER	CLERK		7782.0	1982/01/23	1300	NaN	10	3
    9	7844	TURNER	SALESMAN	7698.0	1981/09/08	1500	0.0	30	2
    1	7499	ALLEN	SALESMAN	7698.0	1981/02/20	1600	300.0	30	1
    6	7782	CLARK	MANAGER		7839.0	1981/01/09	2450	NaN	10	1
    5	7698	BLAKE	MANAGER		7839.0	1981/03/01	2850	NaN	30	4

     

     


    [참고]

    Python Programming
    withmooc = emp.copy()
    
    # 현재 sal의 값을 기준으로 x-50 ~ x+150 사이의 포함된 값에 대한 합계
    withmooc['range_sum'] = withmooc['sal'].apply(lambda x: ( withmooc['sal'][ withmooc['sal'].between(x-50, x + 150, inclusive=True) ] ).sum() )
    
    withmooc.sort_values(by=['sal']).head()
     
    Results
    	empno	ename	job		mgr	hiredate	sal	comm	deptno	range_sum
    0	7369	SMITH	CLERK		7902.0	1980/12/17	800	NaN	20	1750
    11	7900	JAMES	CLERK		7698.0	1981/12/03	950	NaN	30	2050
    10	7876	ADAMS	CLERK		7788.0	1983/01/12	1100	NaN	20	3600
    2	7521	WARD	SALESMAN	7698.0	1981/02/22	1250	500.0	30	3800
    4	7654	MARTIN	SALESMAN	7698.0	1981/09/28	1250	1400.0	30	3800​

     

     


    3) CROSS JOIN

    • emp 테이블을 cross join 후 왼쪽 테이블의 기준 값을 기준으로 오른쪽 테이블에서 x-50 ~ x+150 사이에 포함된 값을 선택 후 카운트
    Python Programming
    pd.merge(emp.assign(foo=1), emp.assign(foo=1), on ='foo').query(' sal_y >= sal_x - 50 & sal_y <= sal_x + 150  ').groupby(['empno_x','sal_x'])['sal_y'].count().head()

     

    Results
    empno_x  sal_x
    7369     800      2
    7499     1600     1
    7521     1250     3
    7566     2975     3
    7654     1250     3
    Name: sal_y, dtype: int64

     

     

     


    3. R Programming (R Package)

     

    R Programming
    %%R
    
    base::summary(emp$empno)
    
    library(plyr)
    
    
    plyr::summarise(emp, Total     = length(empno),                     # empno 변수의 길이
                          count    = sum(!is.na(comm)),                 # na을 제외한 값을 카운트
                          count_1  = length(which(!is.na(emp$comm))),   # na을 제외한 값을 선택 후 해당 값의 길이
                          Managers = sum(!is.na(unique(mgr)))   )       # 중복을 제외한 값을 카운트

     

    Results
      Total count count_1 Managers
    1    14     4       4        6

     


     

    R Programming
    %%R
    
    # Allstars
    sum( !is.na(emp[emp$comm>0 , c("comm")]) )   # comm이 0보다 큰 값을 카운트

     

    Results
    [1] 3

     


    2) 분석함수

     

    R Programming
    %%R
    
    withmooc <- emp
    
    # 현재 sal의 값을 기준으로 x-50 ~ x+150 사이의 값을 카운트
    withmooc['range_cnt'] = sapply(emp$sal, function(x) length(emp$sal[between(emp$sal, x-50, x+150)]))
    
    withmooc[order(withmooc$sal),][1:5,]

     

    Results
    # A tibble: 5 x 9
      empno ename  job        mgr hiredate     sal  comm deptno range_cnt
      <dbl> <chr>  <chr>    <dbl> <date>     <dbl> <dbl>  <dbl>     <int>
    1  7369 SMITH  CLERK     7902 1980-12-17   800    NA     20         2
    2  7900 JAMES  CLERK     7698 1981-12-03   950    NA     30         2
    3  7876 ADAMS  CLERK     7788 1983-01-12  1100    NA     20         3
    4  7521 WARD   SALESMAN  7698 1981-02-22  1250   500     30         3
    5  7654 MARTIN SALESMAN  7698 1981-09-28  1250  1400     30         3

     


    3) CROSS JOIN

    • merge 구문을 사용하여서 emp 테이블을 cross join(self join) 후 왼쪽 테이블의 기준 값을 기준으로 오른쪽 테이블에서 x-50 ~ x+150 사이에 포함된 값을 선택 후 카운트
    R Programming
    %%R
    
    aggregate( sal.y ~ empno.x  + sal.x, data = subset( merge(emp, emp,by= character(),all.x=TRUE, all.y=TRUE), between(sal.y, sal.x -50 , sal.x + 150) ),
               FUN = function(x) c(count_sal = length(x) )
             )[1:5, ]

     

    Results
      empno.x sal.x sal.y
    1    7369   800     2
    2    7900   950     2
    3    7876  1100     3
    4    7521  1250     3
    5    7654  1250     3

     

     


    4. R Dplyr Package

     

    1) 집계함수

    R Programming
    %%R
    
    emp %>%
      tally()  # emp 테이블의 전체 관측치 개수

     

    Results
    # A tibble: 1 x 1
          n
      <int>
    1    14

     


     

    R Programming
    %%R
    
    emp %>%
      dplyr::summarize( Total    = n() ,                            # emp 테이블의 전체 관측치 개수
                        count    = sum(!is.na(comm)),               # na를 제외한 comm값을 카운트
                        Managers = n_distinct(mgr, na.rm = TRUE),   # 중복을 제외한 값을 카운트
                        Managers_1 = sum(!is.na(unique(mgr))) )     # 중복을 제외한 값을 카운트

     

    Results
    # A tibble: 1 x 4
      Total count Managers Managers_1
      <int> <int>    <int>      <int>
    1    14     4        6          6

     


     

    R Programming
    %%R
    
    # Allstars
    emp %>%
      dplyr::filter( comm > 0 ) %>%      # comm가 0보다 큰 데이터 선택
      dplyr::tally()                     # filter로 선택 된 관측치 개수를 카운트

     

    Results
    # A tibble: 1 x 1
          n
      <int>
    1     3

     


    2) 분석함수

    R Programming
    %%R
    
    # 함수를 사용하여서 현재 sal의 값을 기준으로 x-50 ~ x+150 사이의 값을 선택 후 카운트
    emp %>% 
      dplyr::arrange(sal) %>%
      dplyr::mutate( year_guess = sapply(sal, function(x) length(sal[sal >= x - 50 & sal <= x + 150])) ) %>%
      head()

     

    Results
    # A tibble: 6 x 9
      empno ename  job        mgr hiredate     sal  comm deptno year_guess
      <dbl> <chr>  <chr>    <dbl> <date>     <dbl> <dbl>  <dbl>      <int>
    1  7369 SMITH  CLERK     7902 1980-12-17   800    NA     20          2
    2  7900 JAMES  CLERK     7698 1981-12-03   950    NA     30          2
    3  7876 ADAMS  CLERK     7788 1983-01-12  1100    NA     20          3
    4  7521 WARD   SALESMAN  7698 1981-02-22  1250   500     30          3
    5  7654 MARTIN SALESMAN  7698 1981-09-28  1250  1400     30          3
    6  7934 MILLER CLERK     7782 1982-01-23  1300    NA     10          3

     


     

    • 함수를 사용하여서 현재 sal의 값을 기준으로 x-50 ~ x+150 사이의 값(between)을 선택 후 카운트
    R Programming
    %%R
    
    emp %>% 
      dplyr::rowwise()    %>% 
      dplyr::mutate(nhdd = sapply(sal, function(x) length(sal[between(sal, x -50, x + 150 )])) ) %>%
      dplyr::arrange(sal) %>%
      head()

     

    Results
    # A tibble: 6 x 9
    # Rowwise: 
      empno ename  job        mgr hiredate     sal  comm deptno  nhdd
      <dbl> <chr>  <chr>    <dbl> <date>     <dbl> <dbl>  <dbl> <int>
    1  7369 SMITH  CLERK     7902 1980-12-17   800    NA     20     1
    2  7900 JAMES  CLERK     7698 1981-12-03   950    NA     30     1
    3  7876 ADAMS  CLERK     7788 1983-01-12  1100    NA     20     1
    4  7521 WARD   SALESMAN  7698 1981-02-22  1250   500     30     1
    5  7654 MARTIN SALESMAN  7698 1981-09-28  1250  1400     30     1
    6  7934 MILLER CLERK     7782 1982-01-23  1300    NA     10     1

     


     

    • 함수를 사용하여서 현재 sal의 값을 기준으로 x-50 ~ x+150 사이에 속하는 급여(sal)의 합계를 계산
    R Programming
    %%R
    
    emp  %>% 
      dplyr::rowwise() %>% 
      dplyr::mutate(nhdd = sum(emp$sal[between(emp$sal, emp$sal-50, emp$sal + 50 )])) %>%
      head()

     

    Results
    # A tibble: 6 x 9
    # Rowwise: 
      empno ename  job        mgr hiredate     sal  comm deptno  nhdd
      <dbl> <chr>  <chr>    <dbl> <date>     <dbl> <dbl>  <dbl> <dbl>
    1  7369 SMITH  CLERK     7902 1980-12-17   800    NA     20 29025
    2  7499 ALLEN  SALESMAN  7698 1981-02-20  1600   300     30 29025
    3  7521 WARD   SALESMAN  7698 1981-02-22  1250   500     30 29025
    4  7566 JONES  MANAGER   7839 1981-04-02  2975    NA     20 29025
    5  7654 MARTIN SALESMAN  7698 1981-09-28  1250  1400     30 29025
    6  7698 BLAKE  MANAGER   7839 1981-03-01  2850    NA     30 29025

     


    [참고] dbplyr 패키지

    : SQL 쿼리 자동 생성하여 동일한 결과를 산출하는 SQL 쿼리를 확인 할 수 있다. (현재 count(*) 와 RANGE BETWEEN 에 대하여 생성 안되는 듯)

    R Programming
    %%R
    
    # install.packages("dbplyr")
    library(dbplyr)
    
    memdb_frame(emp) %>%   # lazy_frame(emp)
      dbplyr::window_frame(-50, 150)    %>%
      dbplyr::window_order(sal)         %>%
      dplyr::summarise( n_count= n() , sal_sum = sum(sal))  %>% 
      show_query()    # sql_build()

     

     

    R Programming
    <SQL>
    SELECT COUNT() AS `n_count`, SUM(`sal`) AS `sal_sum`
    FROM `dbplyr_006`

     


     

    R Programming
    %%R
    
    library(dbplyr)
    
    lazy_frame(emp) %>%            #memdb_frame(emp) %>%
      dbplyr::window_frame(-1, 2)         %>%
      dbplyr::window_order(sal)           %>%
      dplyr::mutate(NEW_COUNT = sum(sal)) %>%
      show_query()

     

    R Programming
    <SQL>
    SELECT `empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`, SUM(`sal`) OVER (ORDER BY `sal` ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING) AS `NEW_COUNT`
    FROM `df`

     


     

    R Programming
    %%R
    
    memdb_frame(emp)                           %>%
      dplyr::arrange ( sal )                   %>% 
      dbplyr::window_order ( sal )             %>% 
      dplyr::mutate ( cumsum = cumsum ( sal )) %>% 
      show_query ()

     

    R Programming
    <SQL>
    SELECT `empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`, SUM(`sal`) OVER (ORDER BY `sal`, `sal` ROWS UNBOUNDED PRECEDING) AS `cumsum`
    FROM (SELECT *
    FROM `dbplyr_008`
    ORDER BY `sal`)

     


     

    R Programming
    %%R
    
    memdb_frame(emp) %>%   # lazy_frame(emp)
      dbplyr::window_frame(-50, 150)    %>%
      dbplyr::window_order(sal)         %>%
      dplyr::summarise( n_count= n() )  %>% 
      show_query()    # sql_build()

     

    R Programming
    <SQL>
    SELECT COUNT() AS `n_count`
    FROM `dbplyr_010`

     


    3) CROSS JOIN

    • full_join 구문을 사용하여서 emp 테이블을 cross join 후 왼쪽 테이블의 기준 값을 기준으로 오른쪽 테이블에서 x-50 ~ x+150 사이에 포함된 값을 선택 후 카운트.
    • full_join 구문으로 cross join을 수행하기 위하여 “by = character()” 를 지정
    R Programming
    %%R
    
    emp %>% 
      dplyr::full_join(emp, by = character())                   %>%
      dplyr::filter( between(sal.y, sal.x - 50, sal.x + 150 ) ) %>%
      dplyr::group_by(empno.x, sal.x)                           %>%
      dplyr::summarize( Range_cnt = length(sal.y) )             %>%
      dplyr::arrange(sal.x)                                     %>%
      head()

     

    Results
    `summarise()` regrouping output by 'empno.x' (override with `.groups` argument)
    # A tibble: 6 x 3
    # Groups:   empno.x [6]
      empno.x sal.x Range_cnt
        <dbl> <dbl>     <int>
    1    7369   800         2
    2    7900   950         2
    3    7876  1100         3
    4    7521  1250         3
    5    7654  1250         3
    6    7934  1300         3

     

     


    5. R sqldf Package

     

    1) 집계함수

    R Programming
    %%R
    
    print( sqldf(" select count(*) as Total FROM emp ") )                    # emp 테이블의 전체 관측치 개수
    
    print( sqldf(" SELECT COUNT(comm) count FROM emp ") )                    # NULL을 제외한 comm의 값을 카운트
    
    print( sqldf(" SELECT COUNT(DISTINCT mgr) Managers FROM emp ") )         # 중복을 제외한 값을 카운트
    
    print( sqldf(" SELECT COUNT(*) Allstars FROM emp WHERE comm > 0 ") )     # 0보다 큰 comm의 값을 카운트

     

    Results
      Total
    1    14
      count
    1     4
      Managers
    1        6
      Allstars
    1        3

     


    2) 분석함수

     

    * RANGE BETWEEN

    • RANGE BETWEEN를 사용하여서 현재 sal의 값을 기준으로 x-50 ~ x+150 사이에 속하는 급여(sal)의 합계를 계산
    R Programming
    %%R
    
    sqldf(" SELECT ename, sal,
                   COUNT(*) OVER ( ORDER BY sal RANGE BETWEEN 50 PRECEDING AND 150 FOLLOWING) AS mov_count
            FROM emp
            ORDER BY sal, ename; ")[1:5, ]

     

    Results
       ename  sal mov_count
    1  SMITH  800         2
    2  JAMES  950         2
    3  ADAMS 1100         3
    4 MARTIN 1250         3
    5   WARD 1250         3

     


    [참고] ROWS BETWEEN

    • ROWS BETWEEN를 사용하여서 현재 sal의 값을 기준으로 x 이전 관측치 ~ x 이후 2개의 관측치의 개수를 카운트
    R Programming
    %%R
    
    sqldf(" SELECT `empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`, 
                   COUNT(*) OVER (ORDER BY `SAL` ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING) AS `xxx`
            FROM emp
            ORDER BY SAL; ")[1:5,]

     

    Results
      empno  ename      job  mgr   hiredate  sal comm deptno xxx
    1  7369  SMITH    CLERK 7902 1980-12-17  800   NA     20   3
    2  7900  JAMES    CLERK 7698 1981-12-03  950   NA     30   4
    3  7876  ADAMS    CLERK 7788 1983-01-12 1100   NA     20   4
    4  7521   WARD SALESMAN 7698 1981-02-22 1250  500     30   4
    5  7654 MARTIN SALESMAN 7698 1981-09-28 1250 1400     30   4

     


    3) 비등가 JOIN

    • 비등가 Cross join을 수행하여서 현재 sal의 값을 기준으로 x-50 ~ x+150 사이의 값을 선택 후 카운트
    R Programming
    %%R
    
    sqldf(" SELECT A.ENAME,A.SAL,B.mov_count
            FROM   EMP A
                   LEFT JOIN ( SELECT a.sal,count(distinct b.empno) mov_count
                               FROM emp a, emp b
                               where  b.sal between a.sal - 50 and a.sal + 150
                               group by a.sal ) B
                        ON A.SAL = B.SAL    
            ORDER BY 2,1")[1:5, ]

     

    Results
       ename  sal mov_count
    1  SMITH  800         2
    2  JAMES  950         2
    3  ADAMS 1100         3
    4 MARTIN 1250         3
    5   WARD 1250         3

     

     


    6. Python pandasql Package

     

    1) 집계함수

    Python Programming
    print( ps.sqldf(" select count(*) as Total FROM emp ") )
    
    print( ps.sqldf("SELECT COUNT(comm) count FROM emp") )
    
    print( ps.sqldf(" SELECT COUNT(DISTINCT mgr) Managers FROM emp ") )
    
    print( ps.sqldf(" SELECT COUNT(*) Allstars FROM emp WHERE comm > 0 ") )

     

    Results
       Total
    0     14
       count
    0      4
       Managers
    0         6
       Allstars
    0         3

     


    2) 분석함수

    • RANGE BETWEEN를 사용하여서 현재 sal의 값을 기준으로 x-50 ~ x+150 사이에 속하는 급여(sal)의 합계를 계산
    Python Programming
    ps.sqldf(" SELECT ename, sal,   \
                      COUNT(*) OVER (ORDER BY sal RANGE BETWEEN 50 PRECEDING AND 150 FOLLOWING) AS mov_count   \
               FROM emp   \
               ORDER BY sal, ename; ").head()

     

    Results
    	ename	sal	mov_count
    0	SMITH	800	2
    1	JAMES	950	2
    2	ADAMS	1100	3
    3	MARTIN	1250	3
    4	WARD	1250	3​

     


    3) 비등가 JOIN

    • 비등가 Cross join을 수행하여서 현재 sal의 값을 기준으로 x-50 ~ x+150 사이의 값을 선택 후 카운트
    Python Programming
    ps.sqldf(" SELECT A.ENAME,A.SAL,B.mov_count                                      \
               FROM   EMP A                                                          \
                      LEFT JOIN ( SELECT a.sal,count(distinct b.empno) mov_count     \
                                  FROM   emp a, emp b                                \
                                  where  b.sal between a.sal - 50 and a.sal + 150    \
                                  group by a.sal ) B                                 \
                           ON A.SAL = B.SAL                                          \
               ORDER BY 2,1 ").head()

     

    Results
    	ename	sal	mov_count
    0	SMITH	800	2
    1	JAMES	950	2
    2	ADAMS	1100	3
    3	MARTIN	1250	3
    4	WARD	1250	3

     

     

     


    7. R data.table Package

     

    1) 집계함수

    R Programming
    %%R
    
    library(data.table)
    
    DT      <- data.table(emp)
    dept_DT <- data.table(dept)
    
    DT[, .(sal_tot_1 = length(empno) ,                       # emp 테이블의 전체 관측치 개수
           sal_tot_2 = sum(!is.na(comm)),                    # na을 제외한 comm의 값을 카운트
           sal_tot_3 = length(which(!is.na(emp$comm))),      # na을 제외한 comm의 값을 카운트
           mgr_tot   = sum(!is.na(unique(mgr))) ) ]          # 중복을 제외한 값을 카운트

     

    Results
       sal_tot_1 sal_tot_2 sal_tot_3 mgr_tot
    1:        14         4         4       6

     


    2) 분석함수

    • 함수를 사용하여서 현재 sal의 값을 기준으로 x-50 ~ x+150 사이의 값(between)을 선택 후 카운트
    R Programming
    %%R
    
    library(data.table)
    
    DT      <- data.table(emp)
    dept_DT <- data.table(dept)
    
    DT <- DT[order(sal),]
    DT[, cumsum := sapply(sal, function(x) length(sal[between(sal, x-50, x+150)])), ][1:5, ]

     

    Results
       empno  ename      job  mgr   hiredate  sal comm deptno cumsum
    1:  7369  SMITH    CLERK 7902 1980-12-17  800   NA     20      2
    2:  7900  JAMES    CLERK 7698 1981-12-03  950   NA     30      2
    3:  7876  ADAMS    CLERK 7788 1983-01-12 1100   NA     20      3
    4:  7521   WARD SALESMAN 7698 1981-02-22 1250  500     30      3
    5:  7654 MARTIN SALESMAN 7698 1981-09-28 1250 1400     30      3

     


    3) CROSS JOIN

    • emp 테이블(DT)을 cross join( = Cartesian Join) 후 왼쪽 테이블의 기준 값을 기준으로 오른쪽 테이블에서 x-50 ~ x+150 사이에 포함된 값을 선택 후 카운트.
    R Programming
    %%R
    
    DT          <- data.table(emp)
    dept_DT     <- data.table(dept)
    
    # DT syntax 방식 Join
    DT[,k:=1][DT[,k:=1], allow.cartesian=TRUE,on = .(k),][ i.sal >= sal -50 & i.sal <= sal + 150 , .('Range_count' = .N) , keyby = .(empno, sal)][1:5, -c("hiredate") ]

     

    Results
       empno  sal Range_count
    1:  7369  800           2
    2:  7499 1600           1
    3:  7521 1250           3
    4:  7566 2975           3
    5:  7654 1250           3

     


     

    R Programming
    %%R
    
    # data.table::merge() syntax 방식 Join
    merge(DT[,k:=1],DT[,k:=1], by="k",allow.cartesian=TRUE)[ sal.y >= sal.x -50 & sal.y <= sal.x + 150 , .('Range_count' = .N) , keyby = .(empno.x, sal.x)][1:5, ]

     

    Results
       empno.x sal.x Range_count
    1:    7369   800           2
    2:    7499  1600           1
    3:    7521  1250           3
    4:    7566  2975           3
    5:    7654  1250           3

     


    4) 비등가 JOIN

    • 기준(왼쪽) 테이블에 x-50 ~ x+150에 속하는 급여(sal)의 최소값과 최대값을 생성 후 비등가 Cross join을 수행하여서 기준 관측치에 생성된 최소값과 최대값 사이에 포함되는 값을 선택 후 카운트
    R Programming
    %%R
    
    DT          <- data.table(emp)
    dept_DT     <- data.table(dept)
    
    DT[, c("sal_min", "sal_max") := list( sal - 50, sal+150)][DT,on = .( sal_min <= sal , sal_max >= sal ), ][  , .('Range_count' = .N) , keyby = .(empno, sal)][1:5, ]

     

    Results
       empno  sal Range_count
    1:  7369  800           2
    2:  7499 1600           1
    3:  7521 1250           3
    4:  7566 2975           3
    5:  7654 1250           3

     


    [참고] Windows 함수 관련 패키지

    : RcppRoll / zoo / slider / zoo::rollapply() / tibbletime::rollify() / tsibble::slide()

    • https://github.com/DavisVaughan/slider
    • frollsum 함수 : data.talble 패키지에서 사용가능한 함수로 빠른 속도로 이동합계을 계산한다.
    • 이동평균(frollmean) / 이동곱(frollprod) / 이동합계(frollsum)
    R Programming
    %%R
    
    # rollmean of single vector and single window
    DT[, sum1 := data.table::frollsum(DT[, sal], 3, fill = 0)][, -c("job","hiredate")][1:10, ]

     

    Results
        empno  ename  mgr  sal comm deptno sal_min sal_max  sum1
     1:  7369  SMITH 7902  800   NA     20     750     950     0
     2:  7499  ALLEN 7698 1600  300     30    1550    1750     0
     3:  7521   WARD 7698 1250  500     30    1200    1400  3650
     4:  7566  JONES 7839 2975   NA     20    2925    3125  5825
     5:  7654 MARTIN 7698 1250 1400     30    1200    1400  5475
     6:  7698  BLAKE 7839 2850   NA     30    2800    3000  7075
     7:  7782  CLARK 7839 2450   NA     10    2400    2600  6550
     8:  7788  SCOTT 7566 3000   NA     20    2950    3150  8300
     9:  7839   KING   NA 5000   NA     10    4950    5150 10450
    10:  7844 TURNER 7698 1500    0     30    1450    1650  9500

     


     

    • 현재 sal의 값을 기준으로 x-50 ~ x+150 사이에 속하는 급여(sal)의 합계를 계산
    R Programming
    %%R
    
    sqldf(" SELECT ename, sal,
                   sum(sal) OVER ( ORDER BY sal RANGE BETWEEN 50 PRECEDING AND 150 FOLLOWING) AS mov_count
            FROM emp
            ORDER BY sal, ename; ")[1:5, ]

     

    Results
       ename  sal mov_count
    1  SMITH  800      1750
    2  JAMES  950      2050
    3  ADAMS 1100      3600
    4 MARTIN 1250      3800
    5   WARD 1250      3800

     


    SQL 자동 생성 기능

    : 아직 “RANGE BETWEEN” 는 지원안하는 듯 함(ROWS BETWEEN는 지원)

    R Programming
    %%R
    
    memdb_frame(emp) %>%   # lazy_frame(emp) %>%
      dbplyr::window_frame(-50, 150)    %>%
      dbplyr::window_order(sal)         %>%
      dplyr::mutate(mov_sum = sum(sal)) %>%
      show_query()

     

    Results
    <SQL>
    SELECT `empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`, SUM(`sal`) OVER (ORDER BY `sal` ROWS BETWEEN 50 PRECEDING AND 150 FOLLOWING) AS `mov_sum`
    FROM `dbplyr_011`

     


    8. Python DuckDB의 SQL

     

    1) 집계 함수

     

    Python Programming
    %%sql
      SELECT COUNT(*) "Total" FROM emp;

     

    Python Programming
    duckdb.sql(" SELECT COUNT(*) as Total FROM emp ").df()

     

    Results
       Total
    0     14

     


    Python Programming
    %%sql
      SELECT COUNT(comm)         as count_1,
             COUNT(DISTINCT mgr) as Managers_cnt
      FROM emp;

     

    Python Programming
    duckdb.sql(" SELECT COUNT(comm)         as count_1,              \
                        COUNT(DISTINCT mgr) as Managers_cnt          \
                 FROM emp ").df()

     

    Results
       count_1  Managers_cnt
    0        4             6

     


     

    Python Programming
    %%sql
      SELECT COUNT(*) "Allstars" FROM emp WHERE comm > 0;

     

    Python Programming
    duckdb.sql(" SELECT COUNT(*) as Allstars FROM emp WHERE comm > 0 ").df()

     

    Results
     
       Allstars
    0         3

     


    2) 분석 함수

     

    Python Programming
    %%sql
      SELECT ename, sal,
             COUNT(*) OVER (ORDER BY sal RANGE BETWEEN  50 PRECEDING AND
                                                       150 FOLLOWING) AS mov_count
      FROM emp
      ORDER BY sal, ename;

     

    Python Programming
    duckdb.sql(" SELECT ename, sal,                                                                       \
                        COUNT(*) OVER (ORDER BY sal RANGE BETWEEN  50 PRECEDING AND                       \
                                                                  150 FOLLOWING) AS mov_count             \
                 FROM emp                                                                                 \
                 ORDER BY sal, ename                                                                      \
                 LIMIT 6 ").df()

     

     

    Results
        ename   sal  mov_count
    0   SMITH   800          2
    1   JAMES   950          2
    2   ADAMS  1100          3
    3  MARTIN  1250          3
    4    WARD  1250          3
    5  MILLER  1300          3

     


    [참고] ROWS BETWEEN를 사용하여서 현재 sal의 값을 기준으로 x 이전 관측치 ~ x 이후 2개의 관측치의 개수를 카운트

     

    Python Programming
    %%sql
      SELECT a.*,
             COUNT(*) OVER (ORDER BY SAL ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING)  AS PRE_FOL_CNT
      FROM emp a
      ORDER BY SAL

     

    Python Programming
    SELECT a.empno, a.ename, a.job, a.sal,                                                        \
           COUNT(*) OVER (ORDER BY SAL ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING) AS PRE_FOL_CNT   \
    FROM emp a                                                                                    \
    ORDER BY SAL                                                                                  \
    LIMIT 6 ").df()

     

    Results
       empno   ename       job   sal  PRE_FOL_CNT
    0   7369   SMITH     CLERK   800            3
    1   7900   JAMES     CLERK   950            4
    2   7876   ADAMS     CLERK  1100            4
    3   7521    WARD  SALESMAN  1250            4
    4   7654  MARTIN  SALESMAN  1250            4
    5   7934  MILLER     CLERK  1300            4

     


    3) 비등가 조인

     

    Python Programming
    %%sql
      SELECT A.ENAME,A.SAL,B.mov_count
      FROM   emp A
             LEFT JOIN ( SELECT a.sal,count(distinct b.empno) mov_count
                         FROM emp a, emp b
                         where  b.sal between a.sal - 50 and a.sal + 150
                         group by a.sal ) B
                        ON A.SAL = B.SAL
      ORDER BY 2,1

     

    Python Programming
    duckdb.sql(" SELECT A.ENAME,A.SAL,B.mov_count                                         \
                 FROM   emp A                                                             \
                        LEFT JOIN ( SELECT a.sal,count(distinct b.empno) mov_count        \
                                    FROM emp a, emp b                                     \
                                    where  b.sal between a.sal - 50 and a.sal + 150       \
                                    group by a.sal ) B                                    \
                             ON A.SAL = B.SAL                                             \
                 ORDER BY 2,1                                                             \
                 LIMIT 6 ").df() )

     

    Results
        ename   sal  mov_count
    0   SMITH   800          2
    1   JAMES   950          2
    2   ADAMS  1100          3
    3  MARTIN  1250          3
    4    WARD  1250          3
    5  MILLER  1300          3

     


     

    [Oracle, Pandas, R Prog, Dplyr, Sqldf, Pandasql, Data.Table] 오라클 함수와 R & Python 비교 사전 목록? 링크

     

    오라클 SQL 함수(Oracle SQL Function) 목록 리스트 링크

     

    [SQL, Pandas, R Prog, Dplyr, SQLDF, PANDASQL, DATA.TABLE] SQL EMP 예제로 만나는 테이블 데이터 처리 방법 리스트 링크 링크
    반응형

    댓글