Correlação Pontos de História e Lead Time

Este artigo foi inserido no site por meio de um ⁣, pois se trada de um relatório gerado pelo R. Caso não consiga visualizar corretamente, clique aqui para ler de uma outra forma.

Recebi a seguinte pergunta de um dos times que acompanho: devemos parar de fazer estimativas com pontos de histórias na nossa planning? Uma pergunta relativamente comum, mas que demanda um certo tempo para ser respondida.

Será que realmente acertamos as estimativas? Uma história estimada em 8 pontos leva realmente mais tempo para ficar pronta do que uma de 5 pontos? Vale a pena investir 3, 4 horas para estimar um backlog, sem sequer sabermos como está o nível de precisão?

Afinal, existe correlação entre a variável pontos estimados das histórias e o lead time? Neste artigo faço uma breve análise da amostra que recebi e tento responder a pergunta feita no início deste post.

Carregamento de Pacotes e Definição do Diretório de Trabalho

if(!require(pacman)) install.packages("pacman")
library(pacman)

pacman::p_load(tidyverse, dplyr, ggplot2, corrplot, psych, tibble)

setwd(dirname(rstudioapi::getActiveDocumentContext()$path)) 
getwd()
## [1] "Removi o diretório de trabalho!"

Importando e visualizando o dataset

dados <- read.csv2('dados.csv', stringsAsFactors = T) # Carregamento do arquivo csv
as_tibble(dados) 
hist
<fct>
estimado
<int>
lead_time
<int>
História 1 3 2
História 2 1 3
História 3 2 4
História 4 5 5
História 5 3 6
História 6 2 7
História 7 5 8
História 8 8 7
História 9 3 7
História 10 2 8

O dataset é composto por 89 observações. Renomeei as histórias na coluna hist, e temos nas colunas seguintes o valor estimado pela sequência de fibonacci (1,2,3,5,8), e o lead time de cada história. Para que tivêssemos uma correlação forte e positiva, quanto mais alta for a estimativa, mais tempo a história/task deveria levar. Será que é isso mesmo que está acontecendo?

Vamos visualizar o data set:

dados %>% ggplot(aes(x=lead_time)) + 
  geom_histogram(binwidth=1, fill="#69b3a2", color="#e9ecef", alpha=0.9)+
  labs(x = "Lead Time", y = "Frequência")+
  scale_x_continuous(breaks = seq(1,20, by = 1))

Vamos ver a tabela agrupada pelas estimativas:

dados %>% group_by(estimado) %>% 
  summarise(média = round(mean(lead_time),2),
            mínimo = min(lead_time),
            máximo = max(lead_time),
            Q80 = quantile(lead_time, 0.80),
            contagem = n())
estimado
<int>
média
<dbl>
mínimo
<int>
máximo
<int>
Q80
<dbl>
contagem
<int>
1 4.75 3 7 5.6 8
2 6.00 1 10 8.0 16
3 6.34 2 12 9.0 29
5 8.21 5 15 9.4 24
8 8.67 4 19 10.6 12

Para toda a amostra:

dados %>% 
  summarise(média = round(mean(lead_time),2),
            mínimo = min(lead_time),
            máximo = max(lead_time),
            #Q1 = quantile(lead_time, 0.25),
            Q80 = quantile(lead_time, 0.80),
            contagem = n())
média
<dbl>
mínimo
<int>
máximo
<int>
Q80
<dbl>
contagem
<int>
6.96 1 19 9 89

A maioria das histórias foi estimada em 3 pontos. E olhando a tabela temos sim coêrencia. As médias são crescentes. Itens com 1 ponto de história tem média menor do que 2, 2 tem menor média do que 3 e assim por diante.

Vamos a mais uma visualização do dataset:

boxplot(dados$lead_time ~ dados$estimado, main="Pontos de História", 
        xlab = "Pontos Estimados", ylab="Esforço")

Vamos agora pegar o lead time mais frequente e verificar como ele foi estimado:

table(dados$lead_time)
## 
##  1  2  3  4  5  6  7  8  9 10 11 12 14 15 19 
##  2  3  5  9 14  8 11 16  7  4  2  3  3  1  1

Lead Time 8, 16 ocorrências e lead time 5, com 14.

Vamos ver como as tasks finalizadas em 8 dias foram estimadas:

dados %>% filter(lead_time ==8)
hist
<fct>
estimado
<int>
lead_time
<int>
História 7 5 8
História 10 2 8
História 12 3 8
História 21 8 8
História 29 5 8
História 30 8 8
História 35 3 8
História 37 2 8
História 43 2 8
História 47 5 8

Notem que não há um padrão aqui. Histórias estimadas em 2 foram realizadas em 8 dias e histórias estimadas em 8 levaram o mesmo tempo.

Vamos ver as histórias concluídas em 5 dias:

dados %>% filter(lead_time ==5)
hist
<fct>
estimado
<int>
lead_time
<int>
História 4 5 5
História 19 3 5
História 31 5 5
História 36 1 5
História 46 3 5
História 51 8 5
História 54 3 5
História 57 2 5
História 63 1 5
História 65 5 5

Novamente não temos padrão. Histórias estimadas em 1 e histórias estimadas em 8 levaram os mesmos 5 dias.

Vamos aos testes

Teste de Shapiro

O Teste de Shapiro-Wilk tem como objetivo avaliar se uma distribuição é semelhante a uma distribuição normal. A distribuição normal também pode ser chamada de gaussiana e sua forma assemelha-se a de um sino. Esse tipo de distribuição é muito importante, por ser frequentemente usada para modelar fenômenos naturais.

Na prática, podemos querer saber, por exemplo, se a idade dos participantes da nossa amostra segue ou não uma distribuição normal. Para isso, podemos usar o teste de Shapiro-Wilk.

Como resultado, o teste retornará a estatística W, que terá um valor de significância associada, o valor-p. Para dizer que uma distribuição é normal, o valor p precisa ser maior do que 0,05.

shapiro.test(dados$estimado)
## 
##  Shapiro-Wilk normality test
## 
## data:  dados$estimado
## W = 0.86396, p-value = 1.544e-07
shapiro.test(dados$lead_time)
## 
##  Shapiro-Wilk normality test
## 
## data:  dados$lead_time
## W = 0.94661, p-value = 0.001121

Neste caso os valores de p nas duas variáveis foram menores que 0,05, logo não apresentam distribuição normal, o que já era de se esperar. Já é um indicativo que não devemos usar a correlação de Pearson. Para que a correlação de Pearson possa ser feita, precisamos da normalidade nas duas variáveis.

  • H0: distribuição dos dados = normal – p > 0,05
  • H1: distribuição dos dados normal – p  0,05

Vamos verificar agora a preseça de Outliers. Outliers vão influenciar diretamente na correlação de Pearson.

boxplot(dados$estimado, main="Pontos de História", 
        ylab="Esforço")

boxplot(dados$lead_time, main="Lead Time",
   ylab="Dias")

Pontos de história, naturalmente não teríamos outliers, mas no Lead Time temos outliers acima do limite superior. Considero aqui também um mal sinal para prosseguir com a correlação de Pearson.

Vamos verificar agora a correlação Linear entre as variáveis.

plot(dados$lead_time, dados$estimado, main = "Correlação Linear",
     xlab = "Lead Time", ylab = "Pontos de Histórias")

Notemos que nós não temos um padrão linear.

Vamos testar a homocedasticidade por meio da construção de um modelo de regressão linear. Vamos fazer uma análise visual.

mod_reg <- lm(lead_time ~ estimado, dados)
par(mfrow=c(1,2))
plot(mod_reg, which = c(1,3))

Temos dois valores quando fazemos um modelo de regressão linear: um previsto pelo modelo e o observado. A subtração destes valores é o resíduo.Pelos gráficos acima não temos homocedasticidade.

Mesmo assim, vamos vamos obter os números da correlação de Pearson (paramétrica).

cor.test(dados$lead_time, dados$estimado, method = "pearson")
## 
##  Pearson's product-moment correlation
## 
## data:  dados$lead_time and dados$estimado
## t = 3.629, df = 87, p-value = 0.0004795
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.1669417 0.5307694
## sample estimates:
##       cor 
## 0.3625922

Coeficiente de Correlação é 0,362 positivo, o que representa uma correlação fraca. No caso:

  • H0: coeficicnte de correlação = 0 – p > 0,05
  • H1: coeficiente de correlação 0 – p  0,05

Neste caso o valor de p é menor de 0,05, vamos concluir que o coeficiente de correlção é de fato diferente de zero. Estatisticamente é diferente de 0. Caso tivéssemos um p maior que 0,05, o 0 estaria incluido no intervalo de confiança, o que não é o caso.

Vamos então calcular a correlação de Spearman (não paramétrica)

cor.test(dados$lead_time, dados$estimado, method = "spearman")
## Warning in cor.test.default(dados$lead_time, dados$estimado, method =
## "spearman"): Cannot compute exact p-value with ties
## 
##  Spearman's rank correlation rho
## 
## data:  dados$lead_time and dados$estimado
## S = 76286, p-value = 0.0007548
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
##       rho 
## 0.3506456

O rho, coeficiente de correlação de spearman, também é positivo, no entanto é um correlação fraca. O valor de p também é menor que 0,05, logo podemos consideram que o rho é estatiticamente difernete de 0.

Kendall é usada para amostras pequenas e quanto temos valore repetidos, empates. Não é o caso aqui também, pois temos uma amostra considera´vel de 89 observações, enretanto temos muitos valors repetidos nas duas colunas.

cor.test(dados$lead_time, dados$estimado, method = "kendall")
## 
##  Kendall's rank correlation tau
## 
## data:  dados$lead_time and dados$estimado
## z = 3.3023, p-value = 0.000959
## alternative hypothesis: true tau is not equal to 0
## sample estimates:
##       tau 
## 0.2734986

Para dar a resposta, vou utilizar o tau de Kendall (não paramétrica), onde temos um p menor que 0,05, o que afirma que estisticamente o coeficiente de kendall é difreente de 0. Logo temos uma correlação fraca positiva.

ggplot(dados, aes(x=lead_time, y=estimado)) +
  labs(x = "Lead Time", y = "Pontos de História") +
  geom_point(size = 3) +
  theme_bw()

O tau de Kendall é mais conservador do que a correlação de Spearman, no entanto se aplica melhor a esta amostra, embora não considero que tenhamos uma amostra pequena.

Minha resposta ao time foi que considerando o tempo que eles estão levando para estimar as histórias e pela correlação fraca (desprezível), embora estatísticamente diferente de 0, sugeri que parassem de ocupar todo esse tempo com estimativas e utilizassem o tempo com os 3c´s do card. Sugeri ainda que fizessem o controle pela quantidade de histórias desenvolvidas na Sprint e parassem de usar pontos de histórias.

Falando como Agile Coach agora, para que ficar tentando acertar estimativas? Não adianta! Estimativas são difícies de acertar é é um esforço de planejamento inútil. Converse e refine suas histórias/tasks, afinal você não tem bola de cristal. #noestimates rules!

Referências:

  • BOEHMKE, Bradley C.. Data Wrangling with R. Switzerland: Springer, 2016. (Use R!).
  • CHAN, Beda. An Algorithm for Lead Time Demand Distributions. The Journal Of The Operational Research Society, [S.L.], v. 33, n. 11, p. 1005, nov. 1982. JSTOR. http://dx.doi.org/10.2307/2581514.
  • LONG, J. D.; TEETOR, Paul. R Cookbook. 2. ed. Califórnia: O’reilly, 2019.
  • IVANOV, Bisser. Cycle time as normal (Gaussian) distribution. 2021. Disponível em: https://kanbanize.com/blog/normal-gaussian-distribution-over-cycle-time/. Acesso em: 29 nov. 2021.
  • OLIVEIRA, Pauo Felipe de; GUERRA, Saulo; MCDONNELL, Robert. Ciência de dados com R: introdução. Brasília: Ibpad, 2018. 240 p.
  • ONLINE, Psicometria. O que é o teste de Shapiro-Wilk? Disponível em: https://psicometriaonline.com.br/o-que-e-o-teste-de-shapiro-wilk/. Acesso em: 29 nov. 2021.
  • PERES, Fernanda. Estatística aplicada a vida real. 2021. Disponível em: https://www.youtube.com/c/FernandaPeres. Acesso em: 29 nov. 2021.
  • PETRIS, Giovanni; PETRONE, Sonia; CAMPAGNOLI, Patrizia. Dynamic Linear Models with R. New York: Springer, 2009. (Use R!).
  • WICKHAN, Hadley; GROLEMUND, Garrett. R para Data Science: importe, arrume, transforme, visualize

Sobre o autor

Rodrigo Zambon
Sólida experiência em Metodologias Ágeis e Engenharia de Software, com mais de 15 anos atuando como professor de Scrum e Kanban. No Governo do Estado do Espírito Santo, gerenciou uma variedade de projetos, tanto na área de TI, como em outros setores. Sou cientista de dados formado pela USP e atualmente estou profundamente envolvido na área de dados, desempenhando o papel de DPO (Data Protection Officer) no Governo.
0 respostas

Deixe uma resposta

Want to join the discussion?
Feel free to contribute!

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *