O que são os dados

## Observations: 12,299
## Variables: 17
## $ TaskNumber           <chr> "1735", "1742", "1971", "2134", "2251", "2283"...
## $ Summary              <chr> "Flag RI on SCM Message Summary screen using m...
## $ Priority             <dbl> 1, 1, 2, 5, 10, 1, 5, 5, 6, 5, 2, 1, 3, 1, 1, ...
## $ RaisedByID           <chr> "58", "58", "7", "50", "46", "13", "13", "13",...
## $ AssignedToID         <chr> "58", "42", "58", "42", "13", "13", "13", "58"...
## $ AuthorisedByID       <chr> "6", "6", "6", "6", "6", "58", "6", "6", "6", ...
## $ StatusCode           <chr> "FINISHED", "FINISHED", "FINISHED", "FINISHED"...
## $ ProjectCode          <chr> "PC2", "PC2", "PC2", "PC2", "PC2", "PC9", "PC2...
## $ ProjectBreakdownCode <chr> "PBC42", "PBC21", "PBC75", "PBC42", "PBC21", "...
## $ Category             <chr> "Development", "Development", "Operational", "...
## $ SubCategory          <chr> "Enhancement", "Enhancement", "In House Suppor...
## $ HoursEstimate        <dbl> 14.00, 7.00, 0.70, 0.70, 3.50, 7.00, 7.00, 7.0...
## $ HoursActual          <dbl> 1.75, 7.00, 0.70, 0.70, 3.50, 7.00, 7.00, 7.00...
## $ DeveloperID          <chr> "58", "42", "58", "42", "13", "13", "43", "58"...
## $ DeveloperHoursActual <dbl> 1.75, 7.00, 0.70, 0.70, 3.50, 7.00, 7.00, 7.00...
## $ TaskPerformance      <dbl> 12.25, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.0...
## $ DeveloperPerformance <dbl> 12.25, 0.00, 0.00, 0.00, 0.00, 0.00, NA, 0.00,...

Entendendo os dados

Data summary
Name Piped data
Number of rows 12299
Number of columns 4
_______________________
Column type frequency:
character 2
numeric 2
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
ProjectCode 0 1 3 4 0 20 0
TaskNumber 0 1 4 5 0 10266 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
HoursEstimate 0 1 10.15 28.84 0.01 1 3 7.0 910.00 ▇▁▁▁▁
HoursActual 0 1 13.18 68.72 0.01 1 3 8.5 2490.16 ▇▁▁▁▁

Temos 20 projetos, com 12299 estimativas_raw. Não há apenas uma estimativa por tarefa, já que há apenas 10266 valores distintos de TaskNumber.

## # A tibble: 1,384 x 2
## # Groups:   TaskNumber [1,384]
##    TaskNumber     n
##    <chr>      <int>
##  1 10605          8
##  2 6889           8
##  3 10089          7
##  4 10974          7
##  5 11056          7
##  6 11270          7
##  7 13124          7
##  8 13190          7
##  9 13253          7
## 10 3812           7
## # ... with 1,374 more rows

1 estimativa por task

Para nossa análise, usaremos uma estimativa por task. Caso haja mais de uma usaremos a média das estimativas_raw:

Dados por time

Qual a relação entre as estimativas e horas reais tomadas na empresa como um todo e em diferentes categorias de tarefa?

# Vamos inicialmente plotar os dados de horas estimadas e horas reais para verificar se existe alguma distorção de valores que precisam ser ajustados.

estimativas_raw %>% ggplot(aes(x = HoursEstimate, y = HoursActual)) + geom_point()

# Com essa plotagem é possível verificar que existem pontos que fogem dos valores normais para essa informação. Para uma melhor análise, vamos plotar um histograma e verificar onde encontra-se a maior frequência de valores tanto para a Hora estimada como para Hora atual.

hist_he <- estimativas_raw %>% ggplot() + geom_histogram(aes(x = HoursEstimate), bins = 20) + ggtitle("Hora estimada da tarefa")

hist_ha <- estimativas_raw %>% ggplot() + geom_histogram(aes(x = HoursActual), bins = 20) + ggtitle("Hora atual da tarefa")

grid.arrange(hist_he, hist_ha, ncol = 2)

# Agora é possível verificar que a maior frequência de valores para Hora estimada encontra-se abaixo de 50h e para a hora atual fica abaixo 25h.

# Vamos agora ajustar os dados retirando da amostra valores que não esteja no intervalo de 0 até 50 para hora estimada e de 0 até 25 para hora atual. Aplicando o filtro (HoursEstimate<=50 & HoursActual<=25), vamo também ajustar a escalas dos eixos para uma escala logarítmica.

estimativas_raw %>% filter((HoursEstimate<=50 & HoursActual<=25)) %>% ggplot() + geom_point(aes(x = HoursEstimate, y = HoursActual), alpha = .5) + ggtitle("Gráfico Hora estimada por Hora atual") + scale_x_log10() + scale_y_log10()

# Para verificar a relação entre as duas variáveis vamos construir um modelo de regressão linear. O comando abaixo irá calcular e montar o modelo para verificar qual a relação do valor da hora estimada em função do valor da variável hora atual.

dados2 <- estimativas_raw

modelo <- lm(log(HoursActual) ~ log(HoursEstimate) , data = dados2)

# Exibindo os valores que foram gerados pelo comando acima, vamos ter:

summary(modelo)
## 
## Call:
## lm(formula = log(HoursActual) ~ log(HoursEstimate), data = dados2)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -5.6546 -0.4141 -0.0894  0.4535  5.5109 
## 
## Coefficients:
##                    Estimate Std. Error t value Pr(>|t|)    
## (Intercept)        0.250778   0.010666   23.51   <2e-16 ***
## log(HoursEstimate) 0.823931   0.005812  141.76   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.9129 on 12297 degrees of freedom
## Multiple R-squared:  0.6204, Adjusted R-squared:  0.6204 
## F-statistic: 2.01e+04 on 1 and 12297 DF,  p-value: < 2.2e-16
# Após a exibição dos dados é possível fazer algumas considerações:

# 1. Para ambos os coeficientes B0 e B1 é possível identificar que ambos apresentam um nível de significância considerável.
# 2. Com um erro padrão de 0.8099 que nos diz que a distância média dos pontos de dados para a linha de ajuste da regressão é de cerca de 80.99%.
# 3. O modeo de regressão apresenta a seguinte expressão: HoursActual = 0.25 + 0.71 HoraEstimate

# Identificando os tipos de categorias existenes:

dados2 %>% group_by(dados2$Category) %>% summarise(cont=n())
## # A tibble: 3 x 2
##   `dados2$Category`  cont
##   <chr>             <int>
## 1 Development        8220
## 2 Management         2105
## 3 Operational        1974
dados2 %>% filter(Category=="Management")
## # A tibble: 2,105 x 17
##    TaskNumber Summary Priority RaisedByID AssignedToID AuthorisedByID StatusCode
##    <chr>      <chr>      <dbl> <chr>      <chr>        <chr>          <chr>     
##  1 3459       Send O~        1 58         58           58             FINISHED  
##  2 3533       Staff ~        1 58         58           58             FINISHED  
##  3 3559       Genera~        1 58         58           58             FINISHED  
##  4 3568       Manage~        1 58         58           58             FINISHED  
##  5 3628       SiP Ma~        1 58         58           58             FINISHED  
##  6 3661       Attatc~        1 58         58           58             FINISHED  
##  7 3689       Genera~        1 58         58           58             FINISHED  
##  8 3705       Arrang~        1 23         58           58             FINISHED  
##  9 3722       SiP Fr~        5 58         58           58             FINISHED  
## 10 3723       SiP Pr~        2 58         58           58             FINISHED  
## # ... with 2,095 more rows, and 10 more variables: ProjectCode <chr>,
## #   ProjectBreakdownCode <chr>, Category <chr>, SubCategory <chr>,
## #   HoursEstimate <dbl>, HoursActual <dbl>, DeveloperID <chr>,
## #   DeveloperHoursActual <dbl>, TaskPerformance <dbl>,
## #   DeveloperPerformance <dbl>
# Para incluir a coluna de "Category" é preciso criar uma nova coluna e fazer uma conversão dos três tipos de Category para valores numéricos, como 1, 2 e 3. O comando abaixo cria uma nova coluna no dataframe dados2 com o nome Categoria2.

dados2$Categoria2 <- ifelse(dados2$Category=="Development",1,ifelse(dados2$Category=="Management",2,3))

# Agora vamos adicionar essa coluna nova coluna no modelo para verificar qual a influência da Categoria na definição da hora estimada.

modelo<-lm(log(HoursActual) ~log(HoursEstimate)+ Categoria2,data=dados2)

# Exibindo os valores que foram gerados pelo comando acima, vamos ter:

summary(modelo)
## 
## Call:
## lm(formula = log(HoursActual) ~ log(HoursEstimate) + Categoria2, 
##     data = dados2)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -5.7115 -0.4236 -0.1102  0.4610  5.4936 
## 
## Coefficients:
##                    Estimate Std. Error t value Pr(>|t|)    
## (Intercept)        0.197030   0.019817   9.943   <2e-16 ***
## log(HoursEstimate) 0.825095   0.005821 141.744   <2e-16 ***
## Categoria2         0.035110   0.010912   3.218   0.0013 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.9126 on 12296 degrees of freedom
## Multiple R-squared:  0.6207, Adjusted R-squared:  0.6206 
## F-statistic: 1.006e+04 on 2 and 12296 DF,  p-value: < 2.2e-16
# Após a exibição dos dados é possível fazer outras considerações:
# 1. É possível verificar que não houve nenhum acréscimo ao R quadrado, metrica que apresenta a representação da variável ao modelo.
# 2. O modeo de regressão apresenta a seguinte expressão: HoursActual = 0.17 + 0.71 HoraEstimate + 0.05 Category
# 3.Não houve nenhuma alteração ao erro padrão do modelo, permandente em 0.809

Equipes com mais desenvolvedores produzem estimativas com mais ou menos erro que equipes menores?

## Aqui vamos utilizar o dataframe por_time que agrupa os desenvolvedores por código de projeto e também já criar duas colunas: erro_medio_abs e estimativas, com isso é possível construir um gráfico que pode identificar como é possível representar o número médio de erros por tamanho da equipe. Vamos utilizar uma plotagem em três dimensões para representar as informações de horas estimadas, erros cometidos e tamanho da equipe.

por_time %>% ggplot() + geom_point(aes(x=erro_medio_abs, y=estimativas, size = devs),alpha=.3) + scale_size(range = c(5, 15)) + ggtitle("Gráfico de Horas estimadas X erro X equipes")

## Numa primeira análise é possível observar que existe uma certa concentração de equipes entre 4 e 8 pessoas, o tamanho da bola, onde é possível verificar uma faixa de erros entre 0 e 25 e números de horas estimadas entre 0 e 1000h, podendo levar a concluir que equipes menores produzem mais erros e tem valores de estimativas até 1000h.

## Então, iremos utilizar as medidas dos coeficientes de correlação, para saber se com eles é possível descobrir a relação existente entre essas variáveis.


## Para melhor explorar os dados dentro do intervalo das horas estimadas e os erros, vamos plotar os dados agora dividindo em dois grupos, com equipes menor ou igual a 8 e maiores que 8.

line1 <- por_time %>% filter(devs<=8)%>% ggplot() + geom_line(aes(x = estimativas, y= erro_medio_abs),color="grey") + ggtitle("Gráfico de Horas estimadas X erro")

line2 <- por_time %>% filter(devs>8)%>% ggplot() + geom_line(aes(x = estimativas, y= erro_medio_abs),color="grey") + ggtitle("Gráfico de Horas estimadas X erro")

grid.arrange(line1,line2, ncol = 2)

## Nessa plotagem agora é possível perceber que as equipes menores apresentam um maior número de erros em relação as maiores, apesar de existir um momento de pico em ambas as equipes.

## Para melhor explicar a existência ou não da corelação entre o tamanho das equipes e a estimativa de horas vamos utilizar os coeficientes de correlação.

Medidascc <- por_time %>% summarise(Medida1=cor(devs, erro_medio_abs,method="pearson"),Medida2=cor(devs, erro_medio_abs, method = "spearman"),Medida3=cor(devs, erro_medio_abs, method = "kendall"))

Medidascc
## # A tibble: 1 x 3
##   Medida1 Medida2 Medida3
##     <dbl>   <dbl>   <dbl>
## 1  -0.137  0.0438  0.0324
## Diante desses coeficientes é possível verificar que sua correlação é muito baixa, quase todos apresentam valores muito próximo a zero, ou seja, não é possível afirmar que o tamanho das equipes pode influenciar na definição da hora estimada.