## Rows: 12,299
## Columns: 17
## $ TaskNumber <chr> "1735", "1742", "1971", "2134", "2251", "2283", …
## $ Summary <chr> "Flag RI on SCM Message Summary screen using met…
## $ Priority <dbl> 1, 1, 2, 5, 10, 1, 5, 5, 6, 5, 2, 1, 3, 1, 1, 8,…
## $ 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", "5…
## $ StatusCode <chr> "FINISHED", "FINISHED", "FINISHED", "FINISHED", …
## $ ProjectCode <chr> "PC2", "PC2", "PC2", "PC2", "PC2", "PC9", "PC2",…
## $ ProjectBreakdownCode <chr> "PBC42", "PBC21", "PBC75", "PBC42", "PBC21", "PB…
## $ Category <chr> "Development", "Development", "Operational", "De…
## $ SubCategory <chr> "Enhancement", "Enhancement", "In House Support"…
## $ HoursEstimate <dbl> 14.00, 7.00, 0.70, 0.70, 3.50, 7.00, 7.00, 7.00,…
## $ 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.00,…
## $ DeveloperPerformance <dbl> 12.25, 0.00, 0.00, 0.00, 0.00, 0.00, NA, 0.00, 0…
| 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
Para nossa análise, usaremos uma estimativa por task. Caso haja mais de uma usaremos a média das estimativas_raw:
estimativas = estimativas_raw %>%
group_by(ProjectCode, TaskNumber, Category, Priority, Summary) %>%
summarise(
HoursEstimate = mean(HoursEstimate),
HoursActual = mean(HoursActual),
DeveloperPerformance = mean(DeveloperPerformance)
) %>%
ungroup()
Para entender melhor os dados, saber o que nos oferecem e se precisaremos fazer alguma modificação antes de analisar, vamos sumarizá-los novamente.
summary(estimativas)
## ProjectCode TaskNumber Category Priority
## Length:10268 Length:10268 Length:10268 Min. : 1.000
## Class :character Class :character Class :character 1st Qu.: 1.000
## Mode :character Mode :character Mode :character Median : 1.000
## Mean : 2.184
## 3rd Qu.: 3.000
## Max. :10.000
##
## Summary HoursEstimate HoursActual DeveloperPerformance
## Length:10268 Min. : 0.010 Min. : 0.010 Min. :-803.8000
## Class :character 1st Qu.: 1.000 1st Qu.: 1.000 1st Qu.: 0.0000
## Mode :character Median : 2.500 Median : 2.500 Median : 0.0000
## Mean : 8.816 Mean : 9.273 Mean : 1.0293
## 3rd Qu.: 7.000 3rd Qu.: 7.000 3rd Qu.: 0.7575
## Max. :910.000 Max. :2490.160 Max. : 402.0000
## NA's :1434
# Classificação de variáveis
Apenas com a sumarização podemos já observar informações interessantes sobre as variáveis quantitativas. A média de horas estimadas é 8.81, enquanto a média de horas já gastas é 9.273. A variável que representa a peformance do desenvolvedor possui alguns valores ‘N/A’, ou seja, para algumas tasks isso não foi registrado, contudo podemos resolver essa falha calculando esse valor (horas estimadas - horas atuais).
estimativas = estimativas %>% mutate( DevPeformance = ifelse(is.na(DeveloperPerformance), HoursEstimate - HoursActual, DeveloperPerformance))
Uma forma interessante de observar melhor nossos dados pode ser a partir dos times (ProjectCode). Vamos ordernar por quantidade de devs no time.
por_time = estimativas_raw %>%
group_by(ProjectCode) %>%
summarise(devs = NROW(unique(DeveloperID)),
erro_medio_abs = mean(abs(HoursEstimate - HoursActual)),
estimativas = n())
a <- arrange(por_time ,desc(devs))
### ordenar
a %>% kable(align = 'c') %>%
kable_styling(bootstrap_options = c('striped', 'hover', 'responsive'))
| ProjectCode | devs | erro_medio_abs | estimativas |
|---|---|---|---|
| PC2 | 16 | 4.150092 | 4553 |
| PC18 | 15 | 14.163224 | 2888 |
| PC9 | 14 | 11.912057 | 1755 |
| PC11 | 11 | 4.604863 | 292 |
| PC14 | 11 | 90.409429 | 105 |
| PC8 | 11 | 6.071094 | 64 |
| PC17 | 10 | 14.981537 | 449 |
| PC7 | 10 | 10.787793 | 145 |
| PC16 | 8 | 9.592105 | 114 |
| PC4 | 8 | 4.953268 | 511 |
| PC5 | 8 | 12.230494 | 648 |
| PC13 | 6 | 8.500351 | 171 |
| PC6 | 6 | 10.708537 | 287 |
| PC3 | 5 | 5.033218 | 87 |
| PC1 | 4 | 18.514444 | 54 |
| PC10 | 4 | 35.907123 | 73 |
| PC12 | 3 | 8.271852 | 54 |
| PC15 | 3 | 1.866667 | 3 |
| PC20 | 2 | 98.684000 | 5 |
| PC19 | 1 | 3.896342 | 41 |
A partir da tabela vemos que o projeto que conta com mais devs é PC2, com 16, e que seu erro médio entre horas gastas e horas estimadas é de mais ou menos 4.1, um valor próximo dos times PC11 (11 devs) e PC4(8 devs) e um valor distante dos times PC20 (2 devs) e PC14(11 devs). É possível questionar se a quantidade de devs pode influenciar no erro das estimativas (pra mais ou pra menos) ou se a quantidade de estimativas pode ter relação com o erro médio, etc. Poderemos usar tais questionamentos para responder as perguntas propostas futuramente.
ggplot(estimativas, aes(y = HoursEstimate, x = HoursActual)) +
geom_point(alpha = 0.4) + labs(title = "Distribuição das horas estimadas pelo tempo real gasto.", x="Horas gastas", y = "Horas estimadas")
Nesse gráfio de dispersão é possível observar alguns outliers, o que pode dificultar a visualização de uma possível relação entre as variáveis usadas. Sabendo que medidas como correlação ou regressão podem ser influenciadas por outliers, vamos aplicar a transformação nas escalas e tentar reduzir o efeito do viés.
ggplot(estimativas, aes(y = HoursEstimate, x = HoursActual)) +
geom_point(alpha = 0.4) + scale_x_log10() + scale_y_log10() + labs(title = "Distribuição das horas estimadas pelo tempo real gasto.", x="log10(Horas gastas)", y = "log10(Horas estimadas)")
Agora, com uma visualização que tenta evitar o viés podemos observar mais facilmente a relação linear entre as duas variáveis exploradas, e aparentemente, pelo formato, existe uma relação positiva. Para confirmar ou não se as variáveis possuem uma relação vamos usar o cálculo de correlação, que pode ser realizado usando três métodos diferentes:
Como temos dados que aparentam ter uma relação linear, vamos eliminar outliers para obter os coeficientes.
semoutliers %>% summarise(spearman = cor(HoursEstimate, HoursActual, method = "spearman"),
pearson = cor(HoursEstimate, HoursActual, method = "pearson"),
kendall = cor(HoursEstimate, HoursActual, method = "kendall")) %>% kable(align = 'c', caption='Coeficientes de correlação Horas Gastas x Horas Estimadas') %>%
kable_styling(bootstrap_options = c('striped', 'hover', 'responsive'))
| spearman | pearson | kendall |
|---|---|---|
| 0.8259529 | 0.7801166 | 0.6960477 |
A partir do coeficiente linear, usando pearson, dizemos que a correlação entre Horas Estimadas e Horas gastas é muito forte e positiva. Respondendo a primeira parte da pergunta: Devido a força da correlação, é muito provavel que quando Horas estimadas aumentam, Horas gastas também aumentam linearmente.
Agora, vamos separar por categoria e tentar observar se o comportamente continua o mesmo independente das mesmas.
ggplot(estimativas, aes(y = HoursEstimate, x = HoursActual)) +
geom_point(alpha = 0.4) +
geom_smooth(method = "lm", se = FALSE) + facet_grid(. ~ Category) + scale_x_log10() + scale_y_log10()
## `geom_smooth()` using formula 'y ~ x'
Para essa segunda parte, já plotamos os gráficos com as transformações das escalas. É possível notar a semelhança, apesar de uma categoria ser mais numerosa que outras. Para saber se a relação continua forte, mesmo divindo por categorias, vamos medir a correlação novamente, mas agora separadamente.
dev <- semoutliers %>% filter(Category == "Development")
man <- semoutliers %>% filter(Category == "Management")
ope <- semoutliers %>% filter(Category == "Operational")
dev %>% summarise(spearman = cor(HoursEstimate, HoursActual, method = "spearman"),
pearson = cor(HoursEstimate, HoursActual, method = "pearson"),
kendall = cor(HoursEstimate, HoursActual, method = "kendall")) %>% kable(align = 'c', caption='Development - 6919 tasks') %>%
kable_styling(bootstrap_options = c('striped', 'hover', 'responsive'))
| spearman | pearson | kendall |
|---|---|---|
| 0.831504 | 0.7732493 | 0.6979937 |
man %>% summarise(spearman = cor(HoursEstimate, HoursActual, method = "spearman"),
pearson = cor(HoursEstimate, HoursActual, method = "pearson"),
kendall = cor(HoursEstimate, HoursActual, method = "kendall")) %>% kable(align = 'c', caption='Management - 1562 tasks') %>%
kable_styling(bootstrap_options = c('striped', 'hover', 'responsive'))
| spearman | pearson | kendall |
|---|---|---|
| 0.7589755 | 0.8405642 | 0.6405298 |
ope %>% summarise(spearman = cor(HoursEstimate, HoursActual, method = "spearman"),
pearson = cor(HoursEstimate, HoursActual, method = "pearson"),
kendall = cor(HoursEstimate, HoursActual, method = "kendall")) %>% kable(align = 'c', caption='Operational- 1787 tasks') %>%
kable_styling(bootstrap_options = c('striped', 'hover', 'responsive'))
| spearman | pearson | kendall |
|---|---|---|
| 0.8499229 | 0.7160461 | 0.7351554 |
Apesar dos coeficientes sofrerem uma variação, a correlação, usando o método de pearson, continua muito forte. Então, mesmo observando as categorias separadamente, podemos afirmar que quando as horas estimadas aumentam, as horas gastas também aumentam.
Inicialmente vamos visualizar a dispersão entre número de devs e erro médio absoluto já com transformação nas escalas já que anteriormente, “no olho” já foi possível observar que erro podia ser parecido mesmo com quantidades diferentes de devs no time.
ggplot(por_time, aes(y = devs, x = erro_medio_abs)) +
geom_point(alpha = 0.4) +
geom_smooth(method = "lm", se = FALSE) + scale_x_log10() + scale_y_log10()
## `geom_smooth()` using formula 'y ~ x'
Já a partir da visualização é possível notar que a correlação entre a quantidade de devs e o erro médio da estimativa e horas realmente gastas chega muito próximo de 0, com a dispersão dos pontos não é possível também dizer claramente se existem outliers. Para confirmar que uma variável influencia muito pouco ou quase nada na outra, vamos medir o os coeficientes de correlação como foi feito na questão anterior.
por_time %>%
summarise(spearman = cor(devs, erro_medio_abs, method = "spearman"),
pearson = cor(devs, erro_medio_abs, method = "pearson"),
kendall = cor(devs, erro_medio_abs, method = "kendall")) %>% kable(align = 'c', caption='Coeficientes de correlação entre Quantidade de Devs x Erro Médio Absoluto') %>%
kable_styling(bootstrap_options = c('striped', 'hover', 'responsive'))
| spearman | pearson | kendall |
|---|---|---|
| 0.0438071 | -0.1365125 | 0.0324443 |
Observando esses resultados agora podemos afirmar que a relação entre quantidade de devs e erro médio absoluto é muito fraca. Assim, respondendo a pergunta inicial, ter mais ou menos devs na equipe não influencia na precisão da estimativa, ou seja no erro médio calculado.