A Swedish spring classic in orienteering 10mila was raced this weeked in Finspång in Skåne. Similar to 2023, NTNUI won the mens 10 leg overnight relay. I took a little stab at their results system at online.10mila.se/, pulled out the data and did two graphs. You can download the excel data with two sheets from here: 10mila leg times & split times. I included the source code in R in case someone is interested in it.
Here are the positions for top 10 teams at each exchange!
And below a very tall image with positions at each split point at each leg.
Source code
[/kode]
library(httr)library(rvest)library(dplyr)library(ggplot2)library(hrbrthemes)library(viridis)osuus <-10looptbl <-tibble(leg =1:10,splits =list(list(1,2,3,4), # 1list(1,2,3,4), # 2list(1,2,3), # 3list(1,2,3,4), # 4list(1,2,3), # 5list(1,2,3), # 6list(1,2,3), # 7list(1,2,3,4), # 8list(1,2,3), # 9list(1,2,3,4)) # 10)# **********************************************************# vaihtoajatd_leg <-tibble()for (leg in1:osuus){# for (leg in 1:10){read_html(paste0("http://online.10mila.se/index2.php?classId=1&legNo=",leg)) %>%html_table(fill =TRUE) %>% .[[5]] %>% .[-1:-2,] %>%as_tibble() -> tmpnames(tmp) <-c("place","name","club","time","diff") tmp$leg <- leg d_leg <-bind_rows(d_leg,tmp)}d_leg %>%mutate(min =as.integer(sub(":.+$", "", time)),s =as.integer(sub("^.+:", "", time)),secs = min *60+ s,diff_min =as.integer(sub(":.+$", "", diff)),diff_s =as.integer(sub("^.+:", "", diff)),diff_secs = diff_min *60+ diff_s ) -> d_leg2leg_info <-tibble(leg =1:10)leg_info %>%mutate(length =case_when( leg ==1~8.9, leg ==2~7.7, leg ==3~8.2, leg ==4~16.3, leg ==5~6.8, leg ==6~6.8, leg ==7~5.8, leg ==8~11.8, leg ==9~13.9, leg ==10~15.1), length_cum =cumsum(length)) -> leg_infod_leg22 <-left_join(d_leg2,leg_info)d_leg_0 <- d_leg22 %>%filter(leg == osuus) %>%mutate(length_cum =0, diff_secs =0,leg =0)d_leg_11 <-bind_rows(d_leg_0, d_leg22)all_legs <- d_leg_11 # for exceltop10 <- d_leg2 %>%filter(leg == osuus) %>%slice(1:10) %>%pull(club)d_leg_111 <- d_leg_11 %>%filter(club %in% top10)ipsum_palette <-c("#d18975", "#8fd175", "#3f2d54", "#75b8d1", "#2d543d", "#c9d175", "#d1ab75", "#d175b8", "#758bd1")ipsum_palette <-c(ipsum_palette,ipsum_palette,ipsum_palette)ggplot(d_leg_111, aes(x = length_cum, y = diff_secs/60, color = club, fill = club)) +geom_line(alpha = .8) +geom_point(shape =21, color ="white", alpha = .8, size =4) +# scale_x_continuous(breaks = leg_info$length_cum , labels = leg_info$leg) + scale_y_reverse() +geom_text(aes(label = place), color ="white", family ="Roboto Condensed", size =2.5, fontface="bold") +theme_ipsum_rc() + ggrepel::geom_text_repel(data = d_leg_111 %>%filter(leg ==max(leg)), aes(label = club), nudge_y =-.2, size =2.7, family ="Roboto Condensed") +theme(legend.position ="none") +# scale_fill_ipsum() + scale_color_ipsum() +scale_fill_manual(values =c(rev(ipsum_palette),"black")) +scale_color_manual(values =c(rev(ipsum_palette),"black")) +labs(title ="Top 25 teams in 10mila 2025",subtitle ="Time difference to lead in each exchange",caption =paste0("Data: online.10mila.se\n",Sys.time()),x ="distance (km)", y ="minutes behind the lead") -> plotggsave(filename ="./kuvat/10mila2025_top_ten_legs_twitter.png", plot, width =8, heigh =6, dpi =120)# **********************************************************# väliajatd_splits <-tibble()for (leg in1:osuus){# for (leg in 1:10){ splits <-unlist(looptbl$splits[leg]) split_dat <-tibble()for (split1 in splits){read_html(paste0("http://online.10mila.se/index2.php?classId=1&legNo=",leg,"&splitNo=",split1)) %>%html_table(fill =TRUE) %>% .[[5]] %>% .[-1:-2,] %>%as_tibble() -> tmpnames(tmp) <-c("place","name","club","time","diff") tmp$leg <- leg tmp$split1 <- split1 split_dat <-bind_rows(split_dat,tmp)} d_splits <-bind_rows(d_splits,split_dat)}d_splits %>%mutate(min =as.integer(sub(":.+$", "", time)),s =as.integer(sub("^.+:", "", time)),secs = min *60+ s,diff_min =as.integer(sub(":.+$", "", diff)),diff_s =as.integer(sub("^.+:", "", diff)),diff_secs = diff_min *60+ diff_s ) -> d_splits2split_info <- d_splits2 %>%count(leg,split1) %>%select(-n)split_info %>%mutate(length =case_when( leg ==1& split1 ==1~2.8, leg ==1& split1 ==2~4.9, leg ==1& split1 ==3~7.9, leg ==1& split1 ==4~8.4, leg ==2& split1 ==1~2.4, leg ==2& split1 ==2~4.5, leg ==2& split1 ==3~6.3, leg ==2& split1 ==4~7.7, leg ==3& split1 ==1~2.4, leg ==3& split1 ==2~4.5, leg ==3& split1 ==3~7.7, leg ==4& split1 ==1~6.3, leg ==4& split1 ==2~8.8, leg ==4& split1 ==3~11.0, leg ==4& split1 ==4~15.8, leg ==5& split1 ==1~1.8, leg ==5& split1 ==2~4.5, leg ==5& split1 ==3~6.3, leg ==6& split1 ==1~1.8, leg ==6& split1 ==2~4.5, leg ==6& split1 ==3~6.3, leg ==7& split1 ==1~1.8, leg ==7& split1 ==2~3.9, leg ==7& split1 ==3~5.3, leg ==8& split1 ==1~3.0, leg ==8& split1 ==2~6.0, leg ==8& split1 ==3~8.5, leg ==8& split1 ==4~11.3, leg ==9& split1 ==1~5.7, leg ==9& split1 ==2~9.8, leg ==9& split1 ==2~13.4, leg ==10& split1 ==1~5.7, leg ==10& split1 ==2~7.8, leg ==10& split1 ==3~10.9, leg ==10& split1 ==4~14.6, )) %>%group_by(leg) %>%mutate(length_cum =cumsum(length)) -> split_infod_splits_10 <-left_join(d_splits2,split_info)d_leg0 <- all_legs %>%filter(leg %in%1:osuus) %>%mutate(length =0, diff_secs =0,leg =1)if (exists("d_leg_10")){d_leg_11 <-bind_rows(all_legs, d_leg_10) } else {d_leg_11 <- all_legs }d_leg_11_0 <- d_leg_11 %>%mutate(length =0, leg = leg +1)legdata <-bind_rows(d_leg_11,d_leg_11_0)d_splits_10 <-bind_rows(d_splits_10,legdata)d_splits_10 <- d_splits_10 %>%filter(leg !=0, leg !=11)d_splits_10$leg2 <-paste0("Leg ", d_splits_10$leg)d_splits_10$leg2 <-factor(d_splits_10$leg2, levels =c("Leg 1","Leg 2","Leg 3","Leg 4","Leg 5","Leg 6","Leg 7","Leg 8","Leg 9","Leg 10"))all_splits <- d_splits_10 # exceliind_splits_10 <- d_splits_10 %>%filter(club %in% top10)d_splits_10 <- d_splits_10 %>%filter(leg %in%1:osuus)ggplot(d_splits_10, aes(x = length, y = diff_secs/60,# y = diff_secs, color = club, fill = club)) +geom_line(alpha = .8) +geom_point(shape =21, color ="white", alpha = .8, size =4) + ggrepel::geom_text_repel(data = d_splits_10 %>%group_by(leg2) %>%filter(length ==max(length)), aes(label =paste0(name,"\n",club)), nudge_y =-.2, nudge_x =1, size =2.6, lineheight = .8, family ="Roboto Condensed") +facet_wrap(~leg2, ncol =1, scales ="free") +# scale_x_continuous(breaks = 0:18, labels = 0:18, limits = c(0,18)) +scale_x_continuous(breaks =0:20, labels =0:20, limits =c(0,20)) +scale_y_reverse() +geom_text(aes(label = place), color ="white", family ="Roboto Condensed", size =2.5, fontface="bold") +theme_ipsum_rc() +theme(legend.position ="none") +scale_fill_manual(values =c(rev(ipsum_palette),"black")) +scale_color_manual(values =c(rev(ipsum_palette),"black")) +# theme(plot.margin = unit(0,0,0,0)) +# theme(margin(t = 0, r = 0, b = 0, l = 0, unit = "pt")) +labs(title = glue::glue("Top 10 teams in 10mila 2025 in legs 1-{osuus}"),subtitle ="Time difference to lead in each split",caption =paste0("Data: online.10mila.se\n",Sys.time()),x ="distance from exchange (km)", y ="minutes behind the lead") -> splitspicggsave(filename = glue::glue("./kuvat/10mila2025_splits_{osuus}.png"), splitspic, width =12, height =5+2.3*osuus, dpi =120)# excel_filelista <-list()legs <-unique(all_splits$leg2)for (l inseq(legs)){ lista[[l]] <- all_splits[all_splits$leg2 == legs[l], ]}writexl::write_xlsx(lista, "10mila2025_results.xlsx")