O tema central desse relatório é o primeiro dia do ano. O objetivo principal é tentar entender um pouco melhor a atividade dos milhares usuários e repositórios do Github nesse dia que, a priori, poderia ser usado para descanso.
Os dados disponibilizados pelo professor não são suficientes para responder as perguntas, uma vez que as observações são agregadas por trimestre. A solução seria processar os dados exportados pelo Github Archive, uma vez que a granularidade é o segundo. Estes logs encontram-se comprimidos no formato gzip e cada linha do arquivo é a serialização em texto de um objeto do tipo JSON. Como este não é um formato ideal para trabalhar em R, a primeira coisa a fazer foi criar uma ferramenta de conversão dos mesmos para CSV.
Para tal, criamos a ferramenta github/archive2csv. A ferramenta descomprime o conteúdo que recebe via entrada padrão e, para cada linha, decodifica o objeto JSON extraindo os campos de interesse. Depois de extraído e tratado o conteúdo, uma linha no formato CSV é então impressa na saída padrão.
Além de facilitar o trabalho de análise, essa conversão também diminui a quantidade de dados manipulados, uma vez que só transportamos campos que iremos utilizar na análise. Vamos apresentar algumas informações sobre os dados para dar uma ideia do dessa diferença:
Data | #Observações | Tamanho json.gz (MB) | Tamanho CSV (MB) |
---|---|---|---|
2012-01-01 | 48,013 | 9.5 | 2.4 |
2013-01-01 | 98,171 | 26 | 4.7 |
2014-01-01 | 167,135 | 46 | 8.1 |
2015-01-01 | 144,090 | 68 | 12 |
Os campos extraídos são:
Vamos começar olhando apenas as quantidades de atividades:
Ano | #Observações |
---|---|
2012 | 48,013 |
2013 | 98,171 |
2014 | 167,135 |
2015 | 144,090 |
Podemos notar que houve um crescimento de 200%+ de 2012 para 2013 e essa taxa quase se manteve entre 2013 e 2014. Já de 2014 para 2015 a atividade diminuiu. Indo um passo mais fundo, vamos comparar a atividade com relação as horas do dia:
summary(data.2012.01.01$Hour)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.00 9.00 16.00 13.95 20.00 23.00
summary(data.2013.01.01$Hour)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0 8.0 13.0 12.4 17.0 23.0
summary(data.2014.01.01$Hour)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.00 8.00 13.00 12.66 18.00 23.00
summary(data.2015.01.01$Hour)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.00 6.00 14.00 12.19 18.00 23.00
Vemos que elas tem características gerais parecidas, por exemplo, a média gira em torno de meio dia. Um fato que podemos notar é que os desenvolvedores estão começando os trabalhos mais cedo a medida que o tempo passa. O gráfico abaixo nos permite ver melhor como são distribuídas as atividades em relação ao dia:
ggplot() +
geom_line(aes(Hour,n, colour=Year), data=count(data.2012.01.01, Year, Hour)) +
geom_point(aes(Hour,n, shape="0", color=Year), data=count(data.2012.01.01, Year, Hour)) +
geom_line(aes(Hour,n, colour=Year), data=count(data.2013.01.01, Year,Hour)) +
geom_point(aes(Hour,n, shape="1", color=Year), data=count(data.2013.01.01, Year, Hour)) +
geom_line(aes(Hour,n, colour=Year), data=count(data.2014.01.01,Year, Hour)) +
geom_point(aes(Hour,n, shape="2", color=Year), data=count(data.2014.01.01, Year, Hour)) +
geom_line(aes(Hour,n, color=Year), data=count(data.2015.01.01, Year,Hour)) +
geom_point(aes(Hour,n, shape="3", color=Year), data=count(data.2015.01.01, Year, Hour)) +
scale_colour_gradient(high = "red", low="green", guide = "legend") +
scale_x_continuous(breaks=c(seq(0,24, by=4))) +
ylab("Atividade (#eventos)") +
xlab("Hora do dia") +
scale_shape(guide="none") # Removing shape legend (guide)
O gráfico confirma que houve aumento da atividade nas primeiras horas do dia a cada ano e mostra que a atividade tende a cair a medida que se aproxima a meia noite UTC. Também deixa clara a enorme diferença nos padrões de atividade no decorrer do dia. Tentamos (sem sucesso) explicar os vales na linha referente ao ano de 2015. Checamos o site currentlydown.com, porém houve interrupção no serviço.
Começamos essa análise chamando a atenção para a quantidade de tipos diferentes no decorrer dos anos:
Ano | #Tipos de Eventos |
---|---|
2012 | 16 |
2013 | 16 |
2014 | 15 |
2015 | 14 |
Além disso, os eventos mudam com o tempo, por exemplo, em 2013 o evento ForkApplyEvent foi removido e o evento PullRequestReviewCommentEvent foi adicionado. Para simplificar a análise, vamos nos ater aos eventos com maior número de ocorrências:
prop <- data.01.01 %>% count(Type) %>% arrange(desc(n)) %>% head(n=3)
prop$Prop <- prop$n / nrow(data.01.01)
prop
Source: local data frame [3 x 3]
Type n Prop
(fctr) (int) (dbl)
1 PushEvent 233588 0.5106764
2 CreateEvent 59528 0.1301417
3 WatchEvent 47498 0.1038414
Podemos notar que os eventos de Push, Create e Watch representaram mais de 70% do total das observações. Focando nesses eventos, vamos avaliar como as observações se distribuem no decorrer dos anos:
c2 <- filter(data.2012.01.01, Type=="PushEvent" | Type=="CreateEvent" | Type=="WatchEvent") %>%
count(Type) %>% arrange(desc(n))
c3 <- filter(data.2013.01.01, Type=="PushEvent" | Type=="CreateEvent" | Type=="WatchEvent") %>%
count(Type) %>% arrange(desc(n))
c4 <- filter(data.2014.01.01, Type=="PushEvent" | Type=="CreateEvent" | Type=="WatchEvent") %>%
count(Type) %>% arrange(desc(n))
c5 <- filter(data.2015.01.01, Type=="PushEvent" | Type=="CreateEvent" | Type=="WatchEvent") %>%
count(Type) %>% arrange(desc(n))
comb <- data.frame(Type=c("Push","Create","Watch"),
`2012`=c2$n, `2013`=c3$n, `2014`=c4$n, `2015`=c5$n,
check.names = F)
comb.m <- melt(comb, id.vars = "Type")
colnames(comb.m) <- c("Type", "Ano", "value")
ggplot(comb.m, aes(x=Type, y=value/1000)) +
xlab("Tipo de evento") +
ylab("#Observações (*1000)") +
geom_bar(aes(fill=Ano),
position = "dodge",
stat="identity")
Interessante notar que os eventos Create permanecem estáveis, enquanto que Push e Watch parecem demonstrar um crescimento parecido (guardadas as devidas proporções).
Optamos por remover os repositórios que não tinham identificadores ou os que tem o identificador “/” pois não tínhamos como associar repositórios válidos a esses valores.
repoCount.2012 <- data.2012.01.01 %>% filter(Repo != "" & Repo != "/") %>% count(Repo) %>% arrange(desc(n))
repoCount.2013 <- data.2013.01.01 %>% filter(Repo != "" & Repo != "/") %>% count(Repo) %>% arrange(desc(n))
repoCount.2014 <- data.2014.01.01 %>% filter(Repo != "" & Repo != "/") %>% count(Repo) %>% arrange(desc(n))
repoCount.2015 <- data.2015.01.01 %>% filter(Repo != "" & Repo != "/") %>% count(Repo) %>% arrange(desc(n))
Vamos começar analisando um resumo das atividades por repositório entre os anos de 2012 e 2015.
Data | Min | Max | Média | Mediana |
---|---|---|---|---|
2012-01-01 | 1 | 8754 | 3.548 | 1 |
2013-01-01 | 1 | 5681 | 3.135 | 1 |
2014-01-01 | 1 | 692 | 3.105 | 2 |
2015-01-01 | 1 | 3688 | 2.903 | 1 |
Notemos que valor máximo de atividades é muito maior que a média em todos os anos considerados Para tentar entender melhor essa diferença, listamos abaixo 5 repositório com mais atividade em cada ano:
head(repoCount.2012,n=5)
Source: local data frame [5 x 2]
Repo n
(fctr) (int)
1 eclipse/eclipse.platform.common 8754
2 bartaz/impress.js 398
3 KohaAloha/koha 286
4 editor/Gable 210
5 fusinv/glpi 198
head(repoCount.2013,n=5)
Source: local data frame [5 x 2]
Repo n
(fctr) (int)
1 demianturner/sgl-test 5681
2 sakai-mirror/melete 206
3 mojombo/jekyll 203
4 mxcl/homebrew 183
5 emberjs/ember.js 162
head(repoCount.2014,n=5)
Source: local data frame [5 x 2]
Repo n
(fctr) (int)
1 sequenceiq/blog 692
2 Khan/khan-exercises 545
3 igrigorik/ga-beacon 286
4 flexdigit/flexdigit.github.com 277
5 sieste/forecast-pi 277
head(repoCount.2015,n=5)
Source: local data frame [5 x 2]
Repo n
(fctr) (int)
1 kinlane/api-stack 3688
2 KenanSulayman/heartbeat 1271
3 cachethq/Cachet 539
4 sakai-mirror/melete 480
5 qdm/qdm.github.io 466
Em 2012, temos que o repositório do projeto Eclipse (eclipse/eclipse.platform.common) aparece no topo com um número de eventos muito maior que o segundo colocado (bartaz/impress.js). Na tentativa de entender melhor a atividade nesse repositório, vamos tentar agregar os tipos de eventos:
data.2012.01.01 %>% filter(Repo == "eclipse/eclipse.platform.common") %>% count(Type)
Source: local data frame [1 x 2]
Type n
(fctr) (int)
1 CreateEvent 8754
Notamos que todos os eventos são do tipo Create, o qual contabiliza a criação de repositórios, tags ou branchs. Tentamos encontrar algum log em [http://git.eclipse.org/c/platform/eclipse.platform.common.git], mas obtivemos sucesso. Nossa suspeita é que o repositório completo foi re-criado e para tal foram usados diversos branches. Uma vez que temos os dados, poderíamos avaliar os dados anexos em eventos do tipo Create, mas deixaremos essa análise a a análise do que aconteceu nos repositórios do anos seguintes para um trabalhos futuros.
Outro fator que nos chama atenção é a diferença entre a média e a mediana. Essa diferença indica que a curva é enviesada. Para entender melhor essa diferença precisamos entender melhor o skewness e a moda dessas distribuições:
mode <- function(v) {
uniqv <- unique(v)
uniqv[which.max(tabulate(match(v, uniqv)))]
}
skew <- skewness
data.frame(
Year=seq(2012,2015),
Skewness=c(skew(repoCount.2012$n), skew(repoCount.2013$n), skew(repoCount.2014$n), skew(repoCount.2015$n)),
Mode=c(mode(repoCount.2012$n), mode(repoCount.2013$n), mode(repoCount.2014$n), mode(repoCount.2015$n))
)
Year Skewness Mode
1 2012 112.15305 1
2 2013 167.57244 1
3 2014 32.96239 1
4 2015 161.63831 1
Em primeiro lugar vemos que a moda e 1 em todos os casos. Sobre o skewness vemos que ele é positivo e, com exceção de 2014, bem grande. Esses dados nos sugerem uma curva de cauda longa, com a cauda em \(1\). Essa hipótese é confirmada no plot abaixo. Para melhor visualização removemos os outliers descritos acima (repositório da plataforma eclipse) e o conjunto aos 500 repositórios com mais atividade.
p.2012 <- ggplot(repoCount.2012[2:500,], aes(reorder(Repo, -n), n)) +
geom_point(size=0.5, alpha=0.5) +
theme(axis.text.x = element_blank()) +
ylab("#Eventos") +
xlab("Repositórios") +
ggtitle("2012")
p.2013 <- ggplot(repoCount.2013[2:500,], aes(reorder(Repo, -n), n)) +
geom_point(size=0.5, alpha=0.5) +
theme(axis.text.x = element_blank()) +
ylab("#Eventos") +
xlab("Repositórios") +
ggtitle("2013")
p.2014 <- ggplot(repoCount.2014[1:500,], aes(reorder(Repo, -n), n)) +
geom_point(size=0.5, alpha=0.5) +
theme(axis.text.x = element_blank()) +
ylab("#Eventos") +
xlab("Repositórios") +
ggtitle("2014")
p.2015 <- ggplot(repoCount.2015[3:500,], aes(reorder(Repo, -n), n)) +
geom_point(size=0.5, alpha=0.5) +
theme(axis.text.x = element_blank()) +
ylab("#Eventos") +
xlab("Repositórios") +
ggtitle("2015")
grid.arrange(p.2012, p.2013,p.2014,p.2015, ncol=2, nrow=2)
Para realizar a comparação escolhemos o dia 20 de Outubro, pois além de ser dia útil é aniversário de Bruno Dias (parceiro que está aqui fazendo esse trabalho também). Foram coletadas 282786 observações.
A ideia é fazer um apanhado geral de como as três dimensões exploradas nesse relatório até agora aparecem num dia útil e como elas se comparam com o dia 01 de janeiro de 2015. Vamos começar com um resumo geral da atividade durante as horas do dia:
ggplot() +
geom_line(aes(Hour,n, colour = "blue"), data=count(data.2015.10.20, Hour)) +
geom_point(aes(Hour,n, shape="2", colour = "blue"), data=count(data.2015.10.20, Hour)) +
geom_line(aes(Hour,n, colour = "red"), data=count(data.2015.01.01,Hour)) +
geom_point(aes(Hour,n, shape="3", colour = "red"), data=count(data.2015.01.01, Hour)) +
scale_x_continuous(breaks=c(seq(0,24, by=2))) +
scale_color_manual(name="Datas", labels = c("20/10/2015", "01/01/2015"), values = c("blue", "red")) +
ylab("Atividade (#eventos)") +
xlab("Hora do dia") +
scale_shape(guide="none") # Removing shape legend (guide)
Notamos que não existe um padrão de atividade. Uma hipótese para esse fato é que o Github é um repositório de código global, o que quer dizer que desenvolvedores podem estar espalhados pelo globo. Não temos os dados dos programadores, mas sem dúvida é dimensão interessante a ser explorada.
O próximo fator a analisar são os tipos de eventos com mais atividades. Abaixo mostramos o código R e o resultado:
prop.2015.10.20 <- data.2015.10.20 %>% count(Type) %>% arrange(desc(n)) %>% head(n=3)
prop.2015.10.20$Prop <- prop.2015.10.20$n / nrow(data.2015.10.20)
prop.2015.10.20
## Source: local data frame [3 x 3]
##
## Type n Prop
## (fctr) (int) (dbl)
## 1 PushEvent 135969 0.4808194
## 2 CreateEvent 37957 0.1342252
## 3 WatchEvent 26761 0.0946334
prop.2015.01.01 <- data.2015.01.01 %>% count(Type) %>% arrange(desc(n)) %>% head(n=3)
prop.2015.01.01$Prop <- prop.2015.01.01$n / nrow(data.2015.01.01)
prop.2015.01.01
## Source: local data frame [3 x 3]
##
## Type n Prop
## (fctr) (int) (dbl)
## 1 PushEvent 79292 0.5502950
## 2 CreateEvent 15664 0.1087098
## 3 WatchEvent 14247 0.0988757
Notamos que os 3 eventos com maior atividade se mantém. Também vemos que esses 3 eventos dominam as atividades, totalizando mais de 50% do total de eventos. Abaixo plotamos como esses eventos se distribuem entre os repositórios:
ggplot() +
geom_point(aes(Repo, n,colour="darkred", alpha=0.7, shape="a"), data=repoCount.2015.10.20.PushEvent[1:1000,]) +
geom_point(aes(Repo, n, colour="yellow", alpha=0.7, shape="a"),data=repoCount.2015.10.20.CreateEvent[1:1000,]) +
geom_point(aes(Repo, n, colour="gray", alpha=0.7, shape="a"), data=repoCount.2015.10.20.WatchEvent[1:1000,]) +
geom_point(aes(Repo, n, colour="darkred", alpha=0.7, shape="b"), data=repoCount.2015.01.01.PushEvent[1:1000,]) +
geom_point(aes(Repo, n, colour="yellow", alpha=0.7,shape="b"),data=repoCount.2015.01.01.CreateEvent[1:1000,]) +
geom_point(aes(Repo, n, colour="gray", alpha=0.7,shape="b"), alpha=0.7, data=repoCount.2015.01.01.WatchEvent[1:1000,]) +
scale_color_manual(name="Tipos de Eventos", labels = c("PushEvent", "CreateEvent", "WatchEvent"), values = c("darkred", "darkblue", "darkgreen")) +
theme(axis.text.x = element_blank()) +
ylab("#Observações") +
xlab("Repositórios") +
scale_y_continuous(limits = c(0,4000), breaks=c(seq(0,4000, by=500))) +
scale_shape_discrete(name="Data", labels = c("20/01/2015", "01/01/2015")) +
scale_alpha_continuous(guide=FALSE)
O gráfico mostra a quantidade de eventos por repositório para os dias 01/01/2015 (círculo) e 20/10/2015 (triângulo). Fica claro que a imensa maioria dos repositórios fica abaixo dos 100 eventos. Também podemos perceber que as distribuições são parecidas. Por fim, vemos abaixo a distribuição ordenada dos eventos por repositório (3 outliers removidos). Notamos a propriedade de cauda longa se mantém.
repoCount.2015.10.20 <- data.2015.10.20 %>% filter(Repo != "" & Repo != "/") %>% count(Repo) %>% arrange(desc(n))
ggplot(repoCount.2015.10.20[4:500,], aes(reorder(Repo, -n), n)) +
geom_jitter(size=0.5, alpha=0.5, height = 30) +
theme(axis.text.x = element_blank()) +
ylab("#Eventos") +
xlab("Repositórios")