377 lines
11 KiB
Plaintext
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}
|