# Load required libraries
library(ggplot2)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(tidyr)
# 1. Construct the complete 2001–2026 dataset
pub_data <- data.frame(
Year = 2001:2026,
`Smart Search (N=45)` = c(1, 1, 1, 1, 2, 1, 1, 0, 1, 0, 1, 2, 1, 3, 3, 1, 0, 2, 2, 1, 5, 1, 6, 5, 3, 0),
`Boolean Search (N=17)` = c(0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 3, 1, 4, 1, 1, 4),
check.names = FALSE
)
# 2. Reshape data into long format for ggplot2
pub_long <- pub_data %>%
pivot_longer(
cols = c(`Smart Search (N=45)`, `Boolean Search (N=17)`),
names_to = "Strategy",
values_to = "Count"
)
# 3. Generate publication-quality grouped bar chart
pub_plot <- ggplot(pub_long, aes(x = factor(Year), y = Count, fill = Strategy)) +
geom_col(position = position_dodge(preserve = "single", width = 0.8), width = 0.7) +
scale_fill_manual(
values = c("Smart Search (N=45)" = "#2B5C8F", "Boolean Search (N=17)" = "#D95F02")
) +
scale_y_continuous(
breaks = seq(0, 7, by = 1),
expand = expansion(mult = c(0, 0.05))
) +
labs(
title = "Publication Year Distribution by Search Strategy",
subtitle = "Comparison of Web of Science Smart Search vs. Structured Boolean Search",
x = "Publication Year",
y = "Record Count",
fill = "Search Strategy"
) +
theme_classic(base_size = 12) +
theme(
plot.title = element_text(face = "bold", size = 14, hjust = 0),
plot.subtitle = element_text(color = "grey30", size = 10, margin = margin(b = 10)),
axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1, color = "black"),
axis.text.y = element_text(color = "black"),
axis.title = element_text(face = "bold"),
legend.position = "top",
legend.title = element_text(face = "bold"),
panel.grid.major.y = element_line(color = "grey90", linewidth = 0.3),
plot.margin = margin(t = 10, r = 15, b = 10, l = 10)
)
# Display graph
print(pub_plot)

###Cohen inter coder relaibility computation###
# Load required libraries
library(readxl)
library(irr)
## Loading required package: lpSolve
library(psych)
## Warning: package 'psych' was built under R version 4.5.3
##
## Attaching package: 'psych'
## The following objects are masked from 'package:ggplot2':
##
## %+%, alpha
# 1. Import and convert directly to a standard data frame
file_path <- "New Web of science coding decision.xlsx"
df <- as.data.frame(read_excel(file_path))
# 2. Select the two coder columns
coder_data <- df[, c("Coder_Sylvia", "Coder_Emmanuel")]
# 3. Method 1: Using 'irr' package
kappa_result <- kappa2(coder_data, weight = "unweighted")
print(kappa_result)
## Cohen's Kappa for 2 Raters (Weights: unweighted)
##
## Subjects = 62
## Raters = 2
## Kappa = 0.871
##
## z = 6.86
## p-value = 6.98e-12
# 4. Method 2: Using 'psych' package with as.matrix() for 95% CIs
kappa_ci <- cohen.kappa(as.matrix(coder_data))
print(kappa_ci)
## Call: cohen.kappa1(x = x, w = w, n.obs = n.obs, alpha = alpha, levels = levels,
## w.exp = w.exp)
##
## Cohen Kappa and Weighted Kappa correlation coefficients and confidence boundaries
## lower estimate upper
## unweighted kappa 0.75 0.87 0.99
## weighted kappa 0.75 0.87 0.99
##
## Number of subjects = 62
###R2a. Retrieval Effectiveness Result.#####
###How do Smart Search and Boolean Search differ in their overall ability to retrieve relevant documents?
library(readxl)
library(dplyr)
# 1. Load the dataset
file_path <- "New Web of science coding decision.xlsx"
df <- read_excel(file_path)
# 2. Categorize strategy and normalize titles
df <- df %>%
mutate(
Strategy = ifelse(grepl("^SM", Doc_ID), "Smart Search", "Boolean Search"),
Title_clean = tolower(trimws(Title))
)
# 3. Determine the Pooled Gold Standard (Total unique relevant articles across both searches)
gold_standard_n <- df %>%
group_by(Title_clean) %>%
summarise(is_relevant = max(Final_Consensus, na.rm = TRUE)) %>%
filter(is_relevant == 1) %>%
nrow()
cat("Total Pooled Gold Standard Relevant Articles:", gold_standard_n, "\n\n")
## Total Pooled Gold Standard Relevant Articles: 24
# 4. Compute Effectiveness Metrics per Search Strategy
effectiveness_summary <- df %>%
group_by(Strategy) %>%
summarise(
`Total Yield (N)` = n(),
`Relevant Retrieved (R)` = sum(Final_Consensus),
`Precision` = round(sum(Final_Consensus) / n(), 4),
`Recall` = round(sum(Final_Consensus) / gold_standard_n, 4)
)
print(effectiveness_summary)
## # A tibble: 2 × 5
## Strategy `Total Yield (N)` `Relevant Retrieved (R)` Precision Recall
## <chr> <int> <dbl> <dbl> <dbl>
## 1 Boolean Search 17 14 0.824 0.583
## 2 Smart Search 45 17 0.378 0.708
###RQ@b. Ranking effectiveness###
library(readxl)
library(dplyr)
# 1. Load dataset
file_path <- "New Web of science coding decision.xlsx"
df <- read_excel(file_path)
# 2. Assign search strategy and normalize titles
df <- df %>%
mutate(
Strategy = ifelse(grepl("^SM", Doc_ID), "Smart Search", "Boolean Search"),
Title_clean = tolower(trimws(Title))
)
# 3. Compute Pooled Gold Standard count (Total unique relevant items)
gold_standard_n <- df %>%
group_by(Title_clean) %>%
summarise(rel = max(Final_Consensus, na.rm = TRUE), .groups = "drop") %>%
filter(rel == 1) %>%
nrow()
# 4. Define core Information Retrieval (IR) metric functions
calc_p_at_k <- function(rel_vec, k) {
sum(rel_vec[1:min(k, length(rel_vec))]) / k
}
calc_dcg <- function(rel_vec, k) {
rel <- rel_vec[1:min(k, length(rel_vec))]
if (length(rel) == 0) return(0)
ranks <- 1:length(rel)
sum(rel / log2(ranks + 1))
}
calc_ndcg <- function(rel_vec, k, total_gold) {
dcg <- calc_dcg(rel_vec, k)
ideal_rel <- numeric(k)
ideal_rel[1:min(k, total_gold)] <- 1
idcg <- calc_dcg(ideal_rel, k)
if (idcg == 0) return(0)
return(dcg / idcg)
}
calc_map <- function(rel_vec, total_gold) {
rel_ranks <- which(rel_vec == 1)
if (length(rel_ranks) == 0) return(0)
precisions <- sapply(rel_ranks, function(r) sum(rel_vec[1:r]) / r)
sum(precisions) / total_gold
}
# 5. Compute comprehensive ranking metrics across cutoffs (10, 15, 17, 20)
ranking_summary <- df %>%
group_by(Strategy) %>%
arrange(Rank, .by_group = TRUE) %>%
summarise(
`Total Yield` = n(),
`P@10` = round(calc_p_at_k(Final_Consensus, 10), 4),
`P@15` = round(calc_p_at_k(Final_Consensus, 15), 4),
`P@17` = round(calc_p_at_k(Final_Consensus, 17), 4),
`P@20` = round(calc_p_at_k(Final_Consensus, 20), 4),
`MAP` = round(calc_map(Final_Consensus, gold_standard_n), 4),
`nDCG@10` = round(calc_ndcg(Final_Consensus, 10, gold_standard_n), 4),
`nDCG@15` = round(calc_ndcg(Final_Consensus, 15, gold_standard_n), 4),
`nDCG@17` = round(calc_ndcg(Final_Consensus, 17, gold_standard_n), 4),
`nDCG@20` = round(calc_ndcg(Final_Consensus, 20, gold_standard_n), 4),
.groups = "drop"
)
print(ranking_summary)
## # A tibble: 2 × 11
## Strategy `Total Yield` `P@10` `P@15` `P@17` `P@20` MAP `nDCG@10` `nDCG@15`
## <chr> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Boolean S… 17 1 0.933 0.824 0.7 0.581 1 0.956
## 2 Smart Sea… 45 0.6 0.6 0.529 0.5 0.444 0.700 0.676
## # ℹ 2 more variables: `nDCG@17` <dbl>, `nDCG@20` <dbl>
###Overlapping serach results######
library(ggplot2)
library(dplyr)
library(ggrepel) # Package to prevent label collision
# 1. Dataset setup
df_dumbbell <- data.frame(
Title = c(
"DRM & Accessibility of E-Resources",
"Comparison of Accessibility & Usability",
"Ensuring Accessibility of E-Resources",
"Assessment of DL Design Guidelines",
"BVI Users' Interactions with DLs",
"Enhancing Usability (Help Features)",
"Help-Seeking Situations (Mobile)"
),
BL_Rank = c(1, 2, 6, 8, 9, 12, 13),
SM_Rank = c(2, 1, 23, 4, 37, 3, 35)
) %>%
mutate(
Title = factor(Title, levels = rev(Title)),
Displaced = ifelse(SM_Rank > 20, "Displaced (>20)", "Top Rank (<=20)")
)
# 2. Plot with automatic label repulsion
ggplot(df_dumbbell) +
# Segment lines
geom_segment(
aes(x = BL_Rank, xend = SM_Rank, y = Title, yend = Title, color = Displaced),
linewidth = 1.2, alpha = 0.8
) +
# Dots
geom_point(aes(x = BL_Rank, y = Title), color = "#1f77b4", size = 4) +
geom_point(aes(x = SM_Rank, y = Title), color = "#ff7f0e", size = 4) +
# Repelled Labels (Prevents overlaps)
geom_text_repel(
aes(x = BL_Rank, y = Title, label = paste0("BL #", BL_Rank)),
color = "#1f77b4", fontface = "bold", size = 3.2,
direction = "y", nudge_y = 0.2, segment.color = NA
) +
geom_text_repel(
aes(x = SM_Rank, y = Title, label = paste0("SM #", SM_Rank)),
color = "#e65100", fontface = "bold", size = 3.2,
direction = "y", nudge_y = -0.2, segment.color = NA
) +
# Cutoff Lines & Text
geom_vline(xintercept = 17, linetype = "dashed", color = "gray50", alpha = 0.7) +
annotate("text", x = 17.5, y = 1.2, label = "Boolean Cutoff (N=17)",
angle = 90, color = "gray40", fontface = "bold", size = 3, hjust = 0) +
geom_vline(xintercept = 45, linetype = "dotted", color = "gray30", alpha = 0.7) +
annotate("text", x = 44.5, y = 1.2, label = "Smart Search Cutoff (N=45)",
angle = 90, color = "gray30", fontface = "bold", size = 3, hjust = 0) +
# Styling
scale_x_continuous(limits = c(-2, 48), breaks = seq(0, 45, by = 5)) +
scale_color_manual(values = c("Displaced (>20)" = "#d62728", "Top Rank (<=20)" = "#1f77b4")) +
theme_minimal(base_size = 11) +
theme(
legend.position = "none",
panel.grid.minor = element_blank(),
panel.grid.major.y = element_blank(),
plot.title = element_text(face = "bold", size = 12),
axis.text.y = element_text(face = "bold", color = "black")
) +
labs(
title = "Positional Displacement Gap of Overlapping Records",
x = "Rank Position (Lower Number = Higher Position)",
y = "Record Description"
)

####P@K plots ######
library(ggplot2)
library(dplyr)
library(gridExtra)
##
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
##
## combine
# 1. Compute P@k and Cumulative Yield in R
df_metrics <- df %>%
group_by(Strategy) %>%
arrange(Rank) %>%
mutate(
Cum_Relevant = cumsum(Final_Consensus),
Precision_at_k = Cum_Relevant / Rank
)
# 2. Precision-at-K Plot
p1 <- ggplot(df_metrics, aes(x = Rank, y = Precision_at_k, color = Strategy, group = Strategy)) +
geom_line(linewidth = 1.1) +
geom_point(size = 2) +
geom_vline(xintercept = 17, linetype = "dashed", color = "gray50") +
scale_color_manual(values = c("Boolean Search" = "#1f77b4", "Smart Search" = "#ff7f0e")) +
scale_y_continuous(limits = c(0, 1.05), breaks = seq(0, 1, 0.2)) +
theme_minimal() +
labs(title = "Precision Decay Across Rank Cutoffs (P@k)", x = "Rank Cutoff (k)", y = "Precision@k")
# 3. Cumulative Discovery Plot
p2 <- ggplot(df_metrics, aes(x = Rank, y = Cum_Relevant, color = Strategy, group = Strategy)) +
geom_line(linewidth = 1.1) +
geom_point(size = 2) +
geom_hline(yintercept = 24, linetype = "dotted", color = "red3") +
scale_color_manual(values = c("Boolean Search" = "#1f77b4", "Smart Search" = "#ff7f0e")) +
scale_y_continuous(limits = c(0, 25), breaks = seq(0, 25, 5)) +
theme_minimal() +
labs(title = "Cumulative Relevance Discovery Curve", x = "Rank Position", y = "Cumulative Relevant Found")
# Display plots side-by-side
grid.arrange(p1, p2, ncol = 2)
