Files
osk.rs/doc/report.Rnw
2026-02-06 10:24:08 +01:00

377 lines
11 KiB
Plaintext

\documentclass[conference,a4paper]{IEEEtran}
\usepackage{graphicx} % for including figures
\usepackage{booktabs} % for nicer tables
\usepackage{float}
\begin{document}
\section{Abstract}\label{abstract}
We evaluated three on-screen keyboard layouts: QWERTY, Dvorak, and Circle. Objective performance, measured in words per minute (WPM), showed a significant main effect of layout. Post-hoc comparisons revealed that QWERTY was significantly faster than both Dvorak and Circle, while no difference was observed between Dvorak and Circle. Total error rate (TER) did not differ significantly between layouts. Subjective workload ratings assessed via NASA-TLX were similar for Dvorak and Circle, but QWERTY was perceived as less demanding. These results indicate that QWERTY offers superior typing speed, whereas error rates and perceived workload are comparable across layouts.
\section{Introduction}\label{introduction}
\section{Keyboard Designs}\label{keyboard-designs}
Three on-screen keyboard layouts were evaluated in this study: QWERTY, Dvorak, and a custom-designed Circle layout.
1. QWERTY: The standard layout commonly used in English typing, serving as a baseline for comparison.
2. Dvorak: An alternative layout designed to increase typing efficiency for physical keyboards
by placing frequently used letters in the home row, minimizing finger movements.
3. Circle Layout: A custom layout developed for this study, in which keys were arranged in a circular pattern. Letters that occur more frequently in English were positioned closer to the center and rendered larger to facilitate faster access. Less frequently used letters were placed toward the periphery and sized smaller, aiming to optimize ergonomic reach and visual salience.
This design allowed us to investigate both established and novel layouts, comparing objective typing performance, error rates, and subjective workload.
\begin{figure}[H]
\centering
\includegraphics[width=0.45\textwidth]{images/qwerty-pic.png}
\caption{QWERTY Keyboard Layout}
\label{fig:qwerty}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=0.45\textwidth]{images/dvorak-pic.png}
\caption{Dvorak Keyboard Layout}
\label{fig:dvorak}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=0.3\textwidth]{images/circle-pic.png}
\caption{Circle Keyboard Layout}
\label{fig:circle}
\end{figure}
\section{Experiment}\label{experiment}
\subsection{Participants}\label{participants}
\subsection{Apparatus}\label{apparatus}
\subsection{Procedure}\label{procedure}
\section{Results}\label{results}
\subsection{Descriptive Statistics}\label{descriptive-statistics}
\subsubsection{Objective Measures}\label{objective-measures}
<<echo=FALSE, message=FALSE>>=
library(knitr)
library(dplyr)
library(tidyr)
# Read the results CSV
results <- read.csv("../data/results.csv", sep=",", header=TRUE)
ter_stats <- results %>%
summarise(
qwerty_min = min(qwerty_ter, na.rm = TRUE),
qwerty_q1 = quantile(qwerty_ter, 0.25, na.rm = TRUE),
qwerty_median = median(qwerty_ter, na.rm = TRUE),
qwerty_mean = mean(qwerty_ter, na.rm = TRUE),
qwerty_q3 = quantile(qwerty_ter, 0.75, na.rm = TRUE),
qwerty_max = max(qwerty_ter, na.rm = TRUE),
dvorak_min = min(dvorak_ter, na.rm = TRUE),
dvorak_q1 = quantile(dvorak_ter, 0.25, na.rm = TRUE),
dvorak_median = median(dvorak_ter, na.rm = TRUE),
dvorak_mean = mean(dvorak_ter, na.rm = TRUE),
dvorak_q3 = quantile(dvorak_ter, 0.75, na.rm = TRUE),
dvorak_max = max(dvorak_ter, na.rm = TRUE),
circle_min = min(circle_ter, na.rm = TRUE),
circle_q1 = quantile(circle_ter, 0.25, na.rm = TRUE),
circle_median = median(circle_ter, na.rm = TRUE),
circle_mean = mean(circle_ter, na.rm = TRUE),
circle_q3 = quantile(circle_ter, 0.75, na.rm = TRUE),
circle_max = max(circle_ter, na.rm = TRUE)
)
ter_tidy <- ter_stats %>%
pivot_longer(
cols = everything(),
names_to = c("layout", ".value"),
names_sep = "_"
)
ter_tidy <- ter_tidy %>%
select(layout, min, q1, median, mean, q3, max)
# Read the results CSV
results <- read.csv("../data/results.csv", sep=",", header=TRUE)
wpm_stats <- results %>%
summarise(
qwerty_min = min(qwerty_wpm, na.rm = TRUE),
qwerty_q1 = quantile(qwerty_wpm, 0.25, na.rm = TRUE),
qwerty_median = median(qwerty_wpm, na.rm = TRUE),
qwerty_mean = mean(qwerty_wpm, na.rm = TRUE),
qwerty_q3 = quantile(qwerty_wpm, 0.75, na.rm = TRUE),
qwerty_max = max(qwerty_wpm, na.rm = TRUE),
dvorak_min = min(dvorak_wpm, na.rm = TRUE),
dvorak_q1 = quantile(dvorak_wpm, 0.25, na.rm = TRUE),
dvorak_median = median(dvorak_wpm, na.rm = TRUE),
dvorak_mean = mean(dvorak_wpm, na.rm = TRUE),
dvorak_q3 = quantile(dvorak_wpm, 0.75, na.rm = TRUE),
dvorak_max = max(dvorak_wpm, na.rm = TRUE),
circle_min = min(circle_wpm, na.rm = TRUE),
circle_q1 = quantile(circle_wpm, 0.25, na.rm = TRUE),
circle_median = median(circle_wpm, na.rm = TRUE),
circle_mean = mean(circle_wpm, na.rm = TRUE),
circle_q3 = quantile(circle_wpm, 0.75, na.rm = TRUE),
circle_max = max(circle_wpm, na.rm = TRUE)
)
wpm_tidy <- wpm_stats %>%
pivot_longer(
cols = everything(),
names_to = c("layout", ".value"),
names_sep = "_"
)
wpm_tidy <- wpm_tidy %>%
select(layout, min, q1, median, mean, q3, max)
@
% TER table
\begin{table}[H]
\centering
\caption{Summary of Total Error Rate (TER)}
<<results='asis', echo=FALSE>>=
kable(ter_tidy, format="latex", booktabs=TRUE)
@
\end{table}
% WPM table
\begin{table}[H]
\centering
\caption{Summary of Words per Minute (WPM)}
<<results='asis', echo=FALSE>>=
kable(wpm_tidy, format="latex", booktabs=TRUE)
@
\end{table}
<<echo=FALSE, results='hide'>>=
# Create figures directory if it doesn't exist
dir.create("../figures", showWarnings=FALSE)
# Helper functions for standard deviation and confidence intervals
mean_sd <- function(x) {
m <- mean(x)
s <- sd(x)
c(mean=m, lower=m-s, upper=m+s)
}
mean_ci <- function(x) {
m <- mean(x)
se <- sd(x)/sqrt(length(x))
ci <- qt(0.975, df=length(x)-1)*se
c(mean=m, lower=m-ci, upper=m+ci)
}
# TER stats
ter_stats <- rbind(
mean_ci(results$qwerty_ter),
mean_ci(results$dvorak_ter),
mean_ci(results$circle_ter)
)
# Save TER barplot as PDF using LaTeX-compatible fonts
suppressMessages(pdf("../figures/ter_plot.pdf"))
bar_pos <- barplot(
ter_stats[,"mean"],
names.arg=c("QWERTY","DVORAK","CIRCLE"),
ylab="Total Error Rate (TER)",
main="TER of layouts",
ylim=c(0, max(ter_stats[,"upper"])*1.1)
)
# Add confidence intervals
arrows(
x0=bar_pos, y0=ter_stats[,"lower"],
x1=bar_pos, y1=ter_stats[,"upper"],
angle=90, code=3, length=0.05
)
dev.off()
# WPM stats
wpm_stats <- rbind(
mean_sd(results$qwerty_wpm),
mean_sd(results$dvorak_wpm),
mean_sd(results$circle_wpm)
)
# Save WPM barplot as PDF using LaTeX-compatible fonts
suppressMessages(pdf("../figures/wpm_plot.pdf"))
bar_pos <- barplot(
wpm_stats[,"mean"],
names.arg=c("QWERTY","DVORAK","CIRCLE"),
ylab="Words per minute (WPM)",
main="WPM of layouts",
ylim=c(0, max(wpm_stats[,"upper"])*1.1)
)
arrows(
x0=bar_pos, y0=wpm_stats[,"lower"],
x1=bar_pos, y1=wpm_stats[,"upper"],
angle=90, code=3, length=0.05
)
dev.off()
@
% Include TER plot
\begin{figure}[H]
\centering
\includegraphics[width=\columnwidth]{../figures/ter_plot.pdf}
\caption{Total Error Rate (TER) by Keyboard Layout}
\end{figure}
% Include WPM plot
\begin{figure}[H]
\centering
\includegraphics[width=\columnwidth]{../figures/wpm_plot.pdf}
\caption{Words per Minute (WPM) by Keyboard Layout}
\end{figure}
\subsubsection{Subjective Measures}\label{subjective-measures}
<<echo=FALSE, results='hide'>>=
# Read NASA-TLX data
nasa <- read.csv("../data/nasaTLX.csv")
nasa$layout <- factor(nasa$layout)
# Save boxplots as PDF using LaTeX-compatible fonts
suppressMessages(pdf("../figures/nasa_boxplots.pdf"))
par(mfrow=c(2,3)) # Arrange plots in 2 rows x 3 columns
boxplot(mental_demand ~ layout, data=nasa, main="Mental Demand")
boxplot(physical_demand ~ layout, data=nasa, main="Physical Demand")
boxplot(performance ~ layout, data=nasa, main="Performance")
boxplot(effort ~ layout, data=nasa, main="Effort")
boxplot(frustration ~ layout, data=nasa, main="Frustration")
par(mfrow=c(1,1))
dev.off()
@
% Include NASA-TLX boxplots
\begin{figure}[H]
\centering
\includegraphics[width=\columnwidth]{../figures/nasa_boxplots.pdf}
\caption{NASA-TLX Scores by Keyboard Layout}
\end{figure}
\subsection{Inferential Statistics}\label{inferential-statistics}
Independent var: Keyboard type
Dependent var:
\begin{itemize}
\item WPM
\item TER
\item NASA-TLX
\end{itemize}
%Anova RM for WPM
<<echo=FALSE, results='hide'>>=
library(tidyr)
# Add participant ID
results$id <- 1:nrow(results)
# --- WPM Long Format ---
wpm_long <- results %>%
select(id, qwerty_wpm, dvorak_wpm, circle_wpm) %>%
pivot_longer(
cols = -id,
names_to = "layout",
values_to = "wpm"
)
wpm_long$id <- factor(wpm_long$id)
wpm_long$layout <- factor(wpm_long$layout,
levels=c("qwerty_wpm","dvorak_wpm","circle_wpm"),
labels=c("QWERTY","DVORAK","CIRCLE"))
# --- RM ANOVA for WPM ---
anova_wpm <- aov(wpm ~ layout + Error(id/layout), data=wpm_long)
@
Anova WPM results:
<<results='asis', echo=FALSE>>=
wpm_tab <- summary(anova_wpm)[[2]][[1]]
wpm_effect <- wpm_tab["layout", , drop=FALSE]
wpm_effect$`Pr(>F)` <- "$p< .001$"
colnames(wpm_effect) <- c("Df", "Sum Sq", "Mean Sq", "F value", "p-value")
kable(wpm_effect,
format="latex",
booktabs=TRUE,
caption="Layout Effect on WPM",
escape=FALSE)
@
%Anova RM for TER
<<echo=FALSE, results='hide'>>=
# --- TER Long Format ---
ter_long <- results %>%
select(id, qwerty_ter, dvorak_ter, circle_ter) %>%
pivot_longer(
cols = -id,
names_to = "layout",
values_to = "ter"
)
ter_long$id <- factor(ter_long$id)
ter_long$layout <- factor(ter_long$layout,
levels=c("qwerty_ter","dvorak_ter","circle_ter"),
labels=c("QWERTY","DVORAK","CIRCLE"))
# --- RM ANOVA for TER ---
anova_ter <- aov(ter ~ layout + Error(id/layout), data=ter_long)
@
Anova TER results:
<<results='asis', echo=FALSE>>=
ter_tab <- summary(anova_ter)[[2]][[1]]
ter_effect <- ter_tab["layout", , drop=FALSE]
colnames(ter_effect) <- c("Df", "Sum Sq", "Mean Sq", "F value", "p-value")
kable(ter_effect,
format="latex",
booktabs=TRUE,
caption="Repeated-Measures ANOVA for TER")
@
% Post-Hoc analysis with bonferroni correction for WPM
<<echo=FALSE, results='hide'>>=
suppressMessages(library(emmeans))
suppressMessages(emm_wpm <- emmeans(anova_wpm, ~ layout))
posthoc <- pairs(emm_wpm, adjust = "bonferroni")
print(posthoc)
@
\subsubsection{Objective Measures}\label{objective-measures-1}
\subsubsection{Subjective Measures}\label{subjective-measures-1}
\section{Discussion}\label{discussion}
\end{document}