Função utl_tableStat do pacote CNTDD

Post

cntdd
Autor

Kléber Formiga Miranda

Data de Publicação

28 de outubro de 2025

Você já precisou analisar como diferentes características das empresas se relacionam entre si?

Neste post, vamos mostrar como a função utl_tableStat, do pacote cntdd, pode ajudar nessa tarefa de forma prática e visual.

Essa função realiza uma análise tridimensional, ou seja, permite observar o comportamento de uma variável numérica considerando dois grupos de referência ao mesmo tempo. Na prática, ela aplica estatísticas descritivas para revelar padrões e possíveis relações entre variáveis contábeis e financeiras. É uma forma alternativa para análise de cluster.

No exemplo fictício que usaremos, o objetivo é identificar qual percentual de Imposto de Renda e CSLL se verifica entre diferentes tipos de empresas: intensivas e não intensivas em imobilizado (classificadas pelo valor do ativo imobilizado) e com alta e baixa receita (segundo o valor da receita).

O primeiro passo é acessar a base de dados. Vamos utilizar a base dt_contabil, disponível no próprio pacote cntdd. Com a função glimpse, do pacote dplyr, é possível visualizar rapidamente a estrutura da base e entender quais variáveis estão disponíveis para análise.

Acompanhe o passo a passo e veja como aplicar essa função para explorar seus dados contábeis de forma mais inteligente.

library(cntdd) # carrega o pacote cntdd
library(dplyr) # Carrega o pacote dplyr
glimpse(dt_contabil)
Rows: 60
Columns: 38
$ empresa           <chr> "alpha", "alpha", "alpha", "alpha", "alpha", "alpha"…
$ ano               <int> 2016, 2017, 2018, 2019, 2020, 2021, 2016, 2017, 2018…
$ caixaEquiv        <int> 5198, 10517, 8803, 14299, 39659, 34950, 638296, 4248…
$ aplicFinanc       <int> 246286, 339427, 235388, 273264, 541474, 236951, 3489…
$ clientesCP        <int> 326524, 348944, 396347, 428123, 620133, 818424, 2848…
$ estoques          <int> 114409, 117527, 156229, 185886, 301247, 466517, 6858…
$ outrosAtvCirc     <int> 38943, 69255, 75196, 113989, 118040, 148544, 425302,…
$ atvCirc           <int> 731360, 885670, 871963, 1015561, 1620553, 1705386, 4…
$ clientesLP        <int> 14162, 11898, 11101, 10772, 2655, 4428, 0, 0, 0, 0, …
$ investimento      <int> 937, 3029, 3442, 3124, 3123, 2963, 198967, 191662, 1…
$ imobilizado       <int> 75651, 70042, 86161, 314902, 327555, 418239, 2064054…
$ intangLiquido     <int> 89019, 82011, 69558, 77717, 859397, 1051033, 116819,…
$ outrosAtvNaoCirc  <int> 28299, 34609, 39993, 41462, 120057, 304066, 669258, …
$ atvNaoCirc        <int> 208068, 201589, 210255, 447977, 1312787, 1780729, 30…
$ forneced          <int> 68809, 108131, 114039, 139769, 413394, 595164, 47003…
$ empFinCP          <int> 81780, 169555, 45542, 163852, 248004, 514541, 964665…
$ outrosPassCirc    <int> 58423, 91836, 105413, 177572, 282452, 463348, 171897…
$ passivoCirc       <int> 209012, 369522, 264994, 481193, 943850, 1573053, 315…
$ empFinLP          <int> 28042, 18657, 69839, 23364, 408834, 39075, 682578, 7…
$ outrosPassNaoCirc <int> 8844, 10457, 10730, 186361, 184069, 225330, 414210, …
$ passivoNaoCirc    <int> 36886, 29114, 80569, 209725, 592903, 264405, 1096788…
$ patLiquido        <int> 693530, 688623, 736655, 772620, 1396587, 1648657, 37…
$ ativoTotal        <int> 939428, 1087259, 1082219, 1463539, 2933340, 3486116,…
$ receita           <int> 1283203, 1408886, 1580984, 1738990, 1647607, 3027870…
$ custoMercVend     <int> 714366, 762921, 845023, 935693, 865520, 1434437, 243…
$ despVendas        <int> 313479, 346107, 392405, 439467, 548811, 915882, 2627…
$ despAdm           <int> 96149, 117866, 145877, 190560, 168007, 327469, 72290…
$ despOperac        <int> 412126, 466152, 537875, 572255, 686700, 1108236, 328…
$ receitaFinanc     <int> 36926, 35051, 41463, 25599, 67862, 43884, 239311, 12…
$ despesaFinanc     <int> 31050, 25420, 63502, 44422, 106750, 110857, 372009, …
$ impostoRenda      <int> 42304, 29475, 28327, 44309, 6186, 62273, -43552, 212…
$ depreciacao       <int> 26733, 33793, 42336, 83180, 83989, 109511, 296241, 3…
$ ebit              <int> 156710, 179811, 198085, 231041, 95386, 485197, 41799…
$ lucroLiquido      <int> 120282, 159966, 147719, 167908, 50312, 357519, 32885…
$ lair              <int> 162586, 189442, 176047, 212218, 56499, 418225, 28529…
$ flxCxOper         <int> 105328, 180074, 122029, 212172, 228155, 250547, 4322…
$ flxCxInvest       <int> -21703, -94960, 74806, -88448, -307860, -11155, -288…
$ flxCxFinanc       <int> -87013, -79850, -199408, -118347, 105061, -241822, 1…

Vimos que a base de dados possui 38 variáveis e 60 linhas e, que para atender nosso interresse vamos precisar apenas do Ativo Imobilizado, Receita, Imposto de Renda e LAIR (para cálculo do percentual).

Vamos criar uma base (objeto) chamada dt_irPorGrupo e, em seguida, vamos aplicar a função summary para ver estatísticas descritivas básicas das variáveis escolhidas.

dt_irPorGrupo <- # define o nome objeto
  dt_contabil %>% # escolhe a base de dados
  select(imobilizado, receita, impostoRenda, lair) # escolhe as variáveis de interesse

summary(dt_irPorGrupo) # faz estatísticas descritivas básicas com a base de dados
  imobilizado         receita          impostoRenda           lair         
 Min.   :  70042   Min.   :  404333   Min.   :-1307943   Min.   :-2075312  
 1st Qu.: 247846   1st Qu.: 1224547   1st Qu.:  -51243   1st Qu.: -121156  
 Median : 579996   Median : 4230867   Median :   23117   Median :   92723  
 Mean   :1431853   Mean   : 7693517   Mean   :    6202   Mean   :  178683  
 3rd Qu.:2162359   3rd Qu.: 8406145   3rd Qu.:   73595   3rd Qu.:  461329  
 Max.   :5490371   Max.   :36533517   Max.   :  645997   Max.   : 1852711  
 NA's   :1         NA's   :1          NA's   :1          NA's   :1         

Identificamos que algumas empresas apresentam observações com valores nulos (indicados por NA em todas as variáveis) e que outras possuem resultados com LAIR e IR negativos. Não entraremos no mérito das causas desses casos agora. Vamos remover essas observações para mostrar, na prática, como aplicar filtros no R e deixar nossa análise mais interessante neste post.

dt_irPorGrupoAjustada <-
  dt_irPorGrupo %>% 
  na.omit() %>% # Remove todos os NAs da base
  filter((lair > 0 & impostoRenda > 0)) %>%   # filtra apenas observações positivas de LAIR e IR
  mutate(
    percentualIR = impostoRenda / lair # cria a variável percentual IR para análise
  )

glimpse(dt_irPorGrupoAjustada)
Rows: 33
Columns: 5
$ imobilizado  <int> 75651, 70042, 86161, 314902, 327555, 418239, 1913395, 195…
$ receita      <int> 1283203, 1408886, 1580984, 1738990, 1647607, 3027870, 667…
$ impostoRenda <int> 42304, 29475, 28327, 44309, 6186, 62273, 212759, 573065, …
$ lair         <int> 162586, 189442, 176047, 212218, 56499, 418225, 803381, 18…
$ percentualIR <dbl> 0.26019460, 0.15558852, 0.16090589, 0.20879002, 0.1094886…
summary(dt_irPorGrupoAjustada)
  imobilizado         receita          impostoRenda         lair        
 Min.   :  70042   Min.   :  404333   Min.   :  2021   Min.   :  15557  
 1st Qu.: 241065   1st Qu.: 1283203   1st Qu.: 23168   1st Qu.: 146583  
 Median : 589275   Median : 5214801   Median : 46256   Median : 286857  
 Mean   :1317364   Mean   : 6524383   Mean   :138464   Mean   : 547212  
 3rd Qu.:1950785   3rd Qu.: 8085892   3rd Qu.:212759   3rd Qu.: 814863  
 Max.   :5265997   Max.   :30215376   Max.   :573065   Max.   :1852711  
  percentualIR    
 Min.   :0.01053  
 1st Qu.:0.16805  
 Median :0.23016  
 Mean   :0.23423  
 3rd Qu.:0.27034  
 Max.   :0.94873  

Como nosso conjunto de dados é pequeno (33 observações), vamos dividir o ativo imobilizado e a receita em três grupos cada. Assim, teremos empresas com baixo nível de imobilizado (imobilizado_1), médio nível (imobilizado_2) e alto nível (imobilizado_3). Da mesma forma, classificaremos as empresas em baixa receita (receita_1), média receita (receita_2) e alta receita (receita_3).

A função utl_tableStat será responsável por cruzar esses grupos e aplicar a estatística descritiva desejada em cada combinação. Neste exemplo, utilizaremos o tamanho da amostra (length), a média (mean) e o desvio-padrão (sd) para avaliar o comportamento do percentual do imposto de renda de cada grupo.

A seguir, apresentamos o uso da função utl_tableStat com a função length.

utl_tableStat(
  bd = dt_irPorGrupoAjustada, # Base de dados
  grp1 = "imobilizado", # Nome da variável do primeiro agrupamento
  n_grp1 = 3, # Número de grupos do primeiro agrupamento
  grp2 = "receita", # Nome da variável do segundo agrupamento
  n_grp2 = 3, # Número de grupos do segundo agrupamento
  value = "percentualIR", # Variável na qual será aplicada a estatística descritiva
  length # Função de interesse
  )
# A tibble: 3 × 4
# Groups:   Stat_percentualIR [3]
  Stat_percentualIR receita_1 receita_2 receita_3
  <fct>                 <int>     <int>     <int>
1 imobilizado_1             9         2        NA
2 imobilizado_2             2         6         3
3 imobilizado_3            NA         3         8

Ao analisar os cruzamentos entre receita e intensidade de imobilizado, percebemos que alguns grupos não apresentam observações. Isso ocorre, por exemplo, nos casos de empresas com baixa receita e alto nível de imobilizado (receita_1 x imobilizado_3) e aquelas com alta receita e baixo imobilizado (receita_3 x imobilizado_1). Esse tipo de ausência é comum quando a base de dados é pequena.

Para corrigir o problema, é possível ampliar a quantidade de dados disponíveis (a alternativa mais viável) ou reduzir o número de grupos de análise — embora isso nem sempre resolva, já que a falta de dispersão nos valores pode concentrar os resultados em poucos segmentos.

Os maiores grupos identificados foram os de empresas com baixa receita e baixo imobilizado (receita_1 x imobilizado_1), com 9 observações, e o de alta receita com alto imobilizado (receita_3 x imobilizado_3), com 8 observações. Esse comportamento é esperado, pois há uma tendência de que quanto maior o investimento em imobilizado, maior também a receita. No entanto, é importante destacar que essa relação não é uma regra e deve ser analisada com cautela.

Agora vamos usar a função utl_tableStat para saber a média do percentual de imposto de renda que cada grupo possui, conforme sua DRE. Usaremos a função mean.

utl_tableStat(
  bd = dt_irPorGrupoAjustada, # Base de dados
  grp1 = "imobilizado", # Nome da variável do primeiro agrupamento
  n_grp1 = 3, # Número de grupos do primeiro agrupamento
  grp2 = "receita", # Nome da variável do segundo agrupamento
  n_grp2 = 3, # Número de grupos do segundo agrupamento
  value = "percentualIR", # Variável na qual será aplicada a estatística descritiva
  mean # Função de interesse
  )
# A tibble: 3 × 4
# Groups:   Stat_percentualIR [3]
  Stat_percentualIR receita_1 receita_2 receita_3
  <fct>                 <dbl>     <dbl>     <dbl>
1 imobilizado_1         0.224     0.555    NA    
2 imobilizado_2         0.166     0.216     0.240
3 imobilizado_3        NA         0.297     0.170

Interessante observar que o grupo mais intensivo em imobilizado e com maior receita apresenta uma carga tributária menor (17,0%) em comparação ao grupo menos intensivo em imobilizado e com menor receita (22,4%). Já a carga tributária mais elevada foi registrada no grupo intermediário, com receita média e baixa intensidade de imobilizado (55,5%). Para verificar se esses dados apresentam grande variação interna, aplicamos novamente a função sd para avaliar o desvio-padrão de cada grupo.

utl_tableStat(
  bd = dt_irPorGrupoAjustada, # Base de dados
  grp1 = "imobilizado", # Nome da variável do primeiro agrupamento
  n_grp1 = 3, # Número de grupos do primeiro agrupamento
  grp2 = "receita", # Nome da variável do segundo agrupamento
  n_grp2 = 3, # Número de grupos do segundo agrupamento
  value = "percentualIR", # Variável na qual será aplicada a estatística descritiva
  sd # Função de interesse
  )
# A tibble: 3 × 4
# Groups:   Stat_percentualIR [3]
  Stat_percentualIR receita_1 receita_2 receita_3
  <fct>                 <dbl>     <dbl>     <dbl>
1 imobilizado_1        0.0416    0.557    NA     
2 imobilizado_2        0.0505    0.0782    0.0559
3 imobilizado_3       NA         0.0280    0.106 

Os resultados mostram que o grupo com menor receita e baixa intensidade de imobilizado possui a menor dispersão (4,0%), o que indica que sua média é mais representativa e consistente. Já o grupo mais intensivo em imobilizado e com maior receita apresenta uma dispersão maior (10,6%), sugerindo maior variação nos valores observados.

Essa análise pode ser aprofundada com a verificação dos valores máximos e mínimos de cada grupo, utilizando as funções min e max. No entanto, deixamos esse próximo passo com você. Faça seus próprios testes e explore suas bases de dados. A função utl_tableStat cumpriu seu papel. Agora é hora de colocar a mão na massa!

Dúvidas como instalar o pacote cntdd? Acesse Aqui

De volta ao topo