Kimiko and I have been climbing for 5 years both indoors and outdoors. We’ve accumulated lots of gear for top rope, sport, and bouldering. The next thing to add to our climbing gear is a beginner’s trad rack.
After taking a trad placement workshop, we decided we want the following:
- 2 sets of cams. One set with low to mid sizes and one set with a full range that includes a couple big boys.
- A set of nuts with a full range of sizes.
- A set of hexes with mid to large sizes.
We compiled a data set that includes gear from Black Diamond, Totoem, DMM, Metolius, and Wild Country based on recommendations from climbing friends.
Prices reflect manufacturer MSRP Q1 2022.
Brands and models shown below for each category.
library(IRanges)
library(tidyverse)
# Wong, B. Points of view: Color blindness. Nat Methods (2011).
bla <- '#000000'
blu <- '#0072b2'
grb <- '#56b4e9'
lir <- '#cc79a7'
gre <- '#009e73'
red <- '#d55e00'
org <- '#e69f00'
yel <- '#f0e442'
gry <- '#BBBBBB'
my_cols <- c(blu,red,gry,gre,org,grb,lir,gry)
gear <- read.delim("https://raw.githubusercontent.com/joemcgirr/rock_climbing/main/trad_rack/placements.txt")
gear$min <- gear$min * 10
gear$max <- gear$max * 10
gear$width <- gear$max - gear$min
gear$brand_model <- paste(gear$brand, gear$model, sep = " / ")
gear$brand_model_type <- paste(gear$brand, gear$model, gear$type, sep = " / ")
gear$size <- as.character(gear$size)
gear$color_code <- str_replace_all(gear$color_code,"bla","#BBBBBB")
gear$color_code <- str_replace_all(gear$color_code,"blu","#0072b2")
gear$color_code <- str_replace_all(gear$color_code,"grb","#56b4e9")
gear$color_code <- str_replace_all(gear$color_code,"lir","#cc79a7")
gear$color_code <- str_replace_all(gear$color_code,"gre","#009e73")
gear$color_code <- str_replace_all(gear$color_code,"red","#d55e00")
gear$color_code <- str_replace_all(gear$color_code,"org","#e69f00")
gear$color_code <- str_replace_all(gear$color_code,"yel","#f0e442")
gear$color_code <- str_replace_all(gear$color_code,"gry","#BBBBBB")
gear$color_code <- str_replace_all(gear$color_code,"pur","#cc79a7")
cams <- filter(gear, type == "cam") %>% pull(brand_model) %>% unique()
nuts <- filter(gear, type == "nut") %>% pull(brand_model) %>% unique()
hexes <- filter(gear, type == "hex") %>% pull(brand_model) %>% unique()
print("cams")
## [1] "cams"
print(cams)
## [1] "Totem / Totem" "Black Diamond / Camelot C4"
## [3] "Wild Country / Friend" "DMM / Dragon Cams"
## [5] "Black Diamond / Camelot Ultralight"
print("nuts")
## [1] "nuts"
print(nuts)
## [1] "DMM / Alloy Offset" "Black Diamond / Stopper"
## [3] "Metolius / Ultralight Curve Nut" "Wild Country / Classic Rock"
## [5] "Wild Country / Rock"
print("hexes")
## [1] "hexes"
print(hexes)
## [1] "Wild Country / Rockcentric - Sling" "DMM / Torque Nut - Sling"
## [3] "Black Diamond / Hexentric - Wired"
Bars show the range of crack sizes that can be used for each piece. For cams, bars are labeled by size. All colors reflect manufacturer colors.
for(cam in cams){
i_gear <- filter(gear, brand_model %in% c(cam))
ir <- IRanges(i_gear$min,width = i_gear$width)
bins <- disjointBins(IRanges(start(ir), end(ir) + 1))
dat <- cbind(as.data.frame(ir), bin = bins)
i_gear$min <- dat$start *0.1
i_gear$max <- dat$end *0.1
i_gear$bin <- dat$bin
i_gear$width <- i_gear$width *0.1
i_gear <- i_gear %>%
mutate(plot_col = ifelse(type == "cam", "blu",ifelse(type == "nut","red","yel"))) %>%
mutate(bin2 = ifelse(type == "cam", 1,ifelse(type == "nut",2,3)))
p1 <- ggplot(i_gear) +
geom_rect(aes(xmin = min, xmax = max,
ymin = bin, ymax = bin + 0.9),fill = i_gear$color_code) +
geom_text(data = i_gear, aes(x=min+((max-min)/2), y = bin+0.5, label = size), size = 3) +
xlab("\nrange (mm)") +
ggtitle(paste("cam = ",cam)) +
theme_minimal() +
xlim(0,200) +
theme(legend.position="none")+
theme(axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank(),
axis.text.x=element_text(size=14))
print(p1)
}
for(nut in nuts){
i_gear <- filter(gear, brand_model %in% c(nut))
ir <- IRanges(i_gear$min,width = i_gear$width)
bins <- disjointBins(IRanges(start(ir), end(ir) + 1))
dat <- cbind(as.data.frame(ir), bin = bins)
i_gear$min <- dat$start *0.1
i_gear$max <- dat$end *0.1
i_gear$bin <- dat$bin
i_gear$width <- i_gear$width *0.1
i_gear <- i_gear %>%
mutate(plot_col = ifelse(type == "cam", "blu",ifelse(type == "nut","red","yel"))) %>%
mutate(bin2 = ifelse(type == "cam", 1,ifelse(type == "nut",2,3)))
p1 <- ggplot(i_gear) +
geom_rect(aes(xmin = min, xmax = max,
ymin = bin, ymax = bin + 0.9, fill = type),fill = i_gear$color_code) +
#scale_fill_manual(values=c(blu,red,yel)) +
scale_fill_manual(values=c("nut" = red)) +
xlab("\nrange (mm)") +
ggtitle(paste("nut = ",nut)) +
theme(legend.position="none")+
theme_minimal() +
xlim(0,40) +
theme(axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank())
print(p1)
}
for(hex in hexes){
i_gear <- filter(gear, brand_model %in% c(hex))
ir <- IRanges(i_gear$min,width = i_gear$width)
bins <- disjointBins(IRanges(start(ir), end(ir) + 1))
dat <- cbind(as.data.frame(ir), bin = bins)
i_gear$min <- dat$start *0.1
i_gear$max <- dat$end *0.1
i_gear$bin <- dat$bin
i_gear$width <- i_gear$width *0.1
i_gear <- i_gear %>%
mutate(plot_col = ifelse(type == "cam", "blu",ifelse(type == "nut","red","yel"))) %>%
mutate(bin2 = ifelse(type == "cam", 1,ifelse(type == "nut",2,3)))
p1 <- ggplot(i_gear) +
geom_rect(aes(xmin = min, xmax = max,
ymin = bin, ymax = bin + 0.9, fill = type),fill = i_gear$color_code) +
#scale_fill_manual(values=c(blu,red,yel)) +
scale_fill_manual(values=c("hex" = yel)) +
xlab("\nrange (mm)") +
ggtitle(paste("hex = ",hex)) +
theme_minimal() +
xlim(0,80) +
theme(legend.position="none")+
theme(axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank())
print(p1)
}
Not all combinations are shown – just the ones that I thought would be interesting given my initial biases from researching gear.
i_cams <- cams[c(1,2,3)]
i_nuts <- nuts[c(1,5)]
i_hexes <- hexes[c(1)]
for(cam in i_cams){
for(nut in i_nuts){
for(hex in i_hexes){
i_gear <- filter(gear, brand_model %in% c(cam,nut,hex))
ir <- IRanges(i_gear$min,width = i_gear$width)
bins <- disjointBins(IRanges(start(ir), end(ir) + 1))
dat <- cbind(as.data.frame(ir), bin = bins)
i_gear$min <- dat$start *0.1
i_gear$max <- dat$end *0.1
i_gear$bin <- dat$bin
i_gear$width <- i_gear$width *0.1
i_gear <- i_gear %>%
mutate(plot_col = ifelse(type == "cam", "blu",ifelse(type == "nut","red","yel"))) %>%
mutate(bin2 = ifelse(type == "cam", 1,ifelse(type == "nut",2,3)))
p1 <- ggplot(i_gear) +
geom_rect(aes(xmin = min, xmax = max,
ymin = bin, ymax = bin + 0.9, fill = type)) +
#scale_fill_manual(values=c(blu,red,yel)) +
scale_fill_manual(values=c("cam" = blu, "nut" = red, "hex" = yel)) +
xlab("\nrange (mm)") +
ggtitle(paste("cam = ",cam,"\n","nut = ",nut,"\n","hex = ",hex,sep = "")) +
theme_minimal() +
theme(axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank())
print(p1)
}
}
}
#brand <- str_split(gear$brand_model," : ")[[1]][1]
#model <- str_split(gear$brand_model," : ")[[1]][2]
MSRP Q1 2022
prices <- gear %>% group_by(brand_model) %>%
summarize(total_price = sum(price), price_per_piece = round(sum(price)/n(),2), type = unique(type))
p1 <- filter(prices, type == "cam") %>%
ggplot(aes(brand_model, total_price)) +
geom_col() +
geom_text(aes(label=total_price), position=position_dodge(width=0.9), vjust=-0.25) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1, size=12),
axis.title.y=element_text(size=14))+
ylab("total price $\n") + xlab("brand / model") + ggtitle("cams")
print(p1)
p1 <- filter(prices, type == "cam") %>%
ggplot(aes(brand_model, price_per_piece)) +
geom_col() +
geom_text(aes(label=price_per_piece), position=position_dodge(width=0.9), vjust=-0.25) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1, size=12),
axis.title.y=element_text(size=14))+
ylab("price per piece $\n") + xlab("brand / model") + ggtitle("cams")
print(p1)
p1 <- filter(prices, type == "nut") %>%
ggplot(aes(brand_model, total_price)) +
geom_col() +
geom_text(aes(label=total_price), position=position_dodge(width=0.9), vjust=-0.25) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1, size=12),
axis.title.y=element_text(size=14))+
ylab("total price $\n") + xlab("brand / model") + ggtitle("nuts")
print(p1)
p1 <- filter(prices, type == "nut") %>%
ggplot(aes(brand_model, price_per_piece)) +
geom_col() +
geom_text(aes(label=price_per_piece), position=position_dodge(width=0.9), vjust=-0.25) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1, size=12),
axis.title.y=element_text(size=14))+
ylab("price per piece $\n") + xlab("brand / model") + ggtitle("nut")
print(p1)
p1 <- filter(prices, type == "hex") %>%
ggplot(aes(brand_model, total_price)) +
geom_col() +
geom_text(aes(label=total_price), position=position_dodge(width=0.9), vjust=-0.25) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1, size=12),
axis.title.y=element_text(size=14))+
ylab("total price $\n") + xlab("brand / model") + ggtitle("hexes")
print(p1)
p1 <- filter(prices, type == "hex") %>%
ggplot(aes(brand_model, price_per_piece)) +
geom_col() +
geom_text(aes(label=price_per_piece), position=position_dodge(width=0.9), vjust=-0.25) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1, size=12),
axis.title.y=element_text(size=14))+
ylab("price per piece $\n") + xlab("brand / model") + ggtitle("hexes")
print(p1)
n_brand_models <- gear %>% filter(type == "cam")%>% pull(brand_model) %>% unique() %>% length()
p1 <- filter(gear, type == "cam") %>%
ggplot(aes(weight, price, color = brand_model)) +
geom_point(size = 4) +
geom_smooth(method = "lm", fill = "NA") +
theme_minimal() +
scale_color_manual(values = my_cols[c(1:n_brand_models)]) +
theme(axis.title.x=element_text(size=14),
axis.title.y=element_text(size=14))+
ggtitle("cams") + xlab("\nweight (g)") + ylab("price ($)\n")
print(p1)
n_brand_models <- gear %>% filter(type == "nut")%>% pull(brand_model) %>% unique() %>% length()
p1 <- filter(gear, type == "nut") %>%
ggplot(aes(weight, price, color = brand_model)) +
geom_point(size = 4) +
geom_smooth(method = "lm", fill = "NA") +
theme_minimal() +
scale_color_manual(values = my_cols[c(1:n_brand_models)]) +
theme(axis.title.x=element_text(size=14),
axis.title.y=element_text(size=14))+
ggtitle("nuts") + xlab("\nweight (g)") + ylab("price ($)\n")
print(p1)
n_brand_models <- gear %>% filter(type == "nut")%>% pull(brand_model) %>% unique() %>% length()
p1 <- filter(gear, type == "hex") %>%
ggplot(aes(weight, price, color = brand_model)) +
geom_point(size = 4) +
geom_smooth(method = "lm", fill = "NA") +
theme_minimal() +
scale_color_manual(values = my_cols[c(1:n_brand_models)]) +
theme(axis.title.x=element_text(size=14),
axis.title.y=element_text(size=14))+
ggtitle("hexes") + xlab("\nweight (g)") + ylab("price ($)\n")
print(p1)
n_brand_models <- gear %>% filter(type == "cam")%>% pull(brand_model) %>% unique() %>% length()
p1 <- filter(gear, type == "cam") %>%
ggplot(aes(weight, strength, color = brand_model)) +
geom_point(size = 4) +
geom_smooth(method = "lm", fill = "NA") +
theme_minimal() +
scale_color_manual(values = my_cols[c(1:n_brand_models)]) +
theme(axis.title.x=element_text(size=14),
axis.title.y=element_text(size=14))+
ggtitle("cams") + xlab("\nweight (g)") + ylab("strength (kN)\n")
print(p1)
n_brand_models <- gear %>% filter(type == "nut")%>% pull(brand_model) %>% unique() %>% length()
p1 <- filter(gear, type == "nut") %>%
ggplot(aes(weight, strength, color = brand_model)) +
geom_point(size = 4) +
geom_smooth(method = "lm", fill = "NA") +
theme_minimal() +
scale_color_manual(values = my_cols[c(1:n_brand_models)]) +
theme(axis.title.x=element_text(size=14),
axis.title.y=element_text(size=14))+
ggtitle("nuts") + xlab("\nweight (g)") + ylab("strength (kN)\n")
print(p1)
n_brand_models <- gear %>% filter(type == "hex")%>% pull(brand_model) %>% unique() %>% length()
p1 <- filter(gear, type == "hex") %>%
ggplot(aes(weight, strength, color = brand_model)) +
geom_point(size = 4) +
geom_smooth(method = "lm", fill = "NA") +
theme_minimal() +
scale_color_manual(values = my_cols[c(1:n_brand_models)]) +
theme(axis.title.x=element_text(size=14),
axis.title.y=element_text(size=14))+
ggtitle("hexes") + xlab("\nweight (g)") + ylab("strength (kN)\n")
print(p1)
Our final decisions
i_cams <- filter(gear, brand_model %in% c(cams[c(1,3)]))
i_nuts1 <- filter(gear, brand_model %in% c(nuts[c(1)]))
i_nuts <- filter(gear, brand_model %in% c(nuts[c(5)]), size %in% c(1:5)) %>% bind_rows(i_nuts1)
i_hexes <- filter(gear, brand_model %in% c(hexes[c(1)]))
i_gear <- bind_rows(i_cams,i_nuts,i_hexes)
ir <- IRanges(i_gear$min,width = i_gear$width)
bins <- disjointBins(IRanges(start(ir), end(ir) + 1))
dat <- cbind(as.data.frame(ir), bin = bins)
i_gear$min <- dat$start *0.1
i_gear$max <- dat$end *0.1
i_gear$bin <- dat$bin
i_gear$width <- i_gear$width *0.1
i_gear <- i_gear %>%
mutate(plot_col = ifelse(type == "cam", "blu",ifelse(type == "nut","red","yel"))) %>%
mutate(bin2 = ifelse(type == "cam", 1,ifelse(type == "nut",2,3)))
p1 <- ggplot(i_gear) +
geom_rect(aes(xmin = min, xmax = max,
ymin = bin, ymax = bin + 0.9, fill = type)) +
#scale_fill_manual(values=c(blu,red,yel)) +
scale_fill_manual(values=c("cam" = blu, "nut" = red, "hex" = yel)) +
xlab("\nrange (mm)") +
ggtitle(paste("cam = WC Friends and Totems","\n",
"nut = Wild Country Rock size 1-5 and DMM Allot Offset 7-11 ","\n",
"hex = Wild Country Rockcentric - Sling size 5-9",sep = "")) +
theme_minimal() +
theme(axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank())
print(p1)
print(paste("cost: $",sum(i_gear$price), sep = ""))
## [1] "cost: $1606.55"