现在我有一个包含 23 个测验问题的数据集,受访者必须按照正确的顺序放置指定的步骤。我使用了 Qualtrics,因此数据的输出方式可以告诉我受访者将每个步骤放在 23 个列表中的位置。 这是数据的精简版本
ResponseID | Step1| Step2 |Step3|.....|Step22|Step23
p1 | 1 | 2 | 3 |.....|22 |23
p2 | 2 | 3 | 4 |.....|23 |1
p3 | 2 | 23 | 7 |.....|12 |17
正确的顺序是数字。因此,任何按顺序排列从 1 到 23 的答案的人都会以正确的顺序获得所有步骤。
实际上,我通过分配分值来对其进行评分,如下所示:
dissdata <- dissdata %>%
mutate(retention1score = ifelse(dissdata$Q9.2_1 == 1, 1, 0) +
ifelse(dissdata$Q9.2_2 == 2, 1, 0) +
ifelse(dissdata$Q9.2_3 == 3, 1, 0) +
ifelse(dissdata$Q9.2_4 == 4, 1, 0) +
ifelse(dissdata$Q9.2_5 == 5, 1, 0) +
ifelse(dissdata$Q9.2_6 == 6, 1, 0).................
ifelse(dissdata$Q9.2_23 == 23, 1, 0))
基于此,上表中的受访者 p1 的得分为 100% 或 23/23。 受访者 p2 和 p3 得 0 分。但正如您所看到的,受访者 2 比受访者 3 稍微正确一些。受访者 p2 在整个测验中都是一次性的,而受访者 3 则完全错误。
所以,这个 if else 方法适用于非常严格的、全有或全无的评分。但它没有考虑到可能落后了 1 步的人,比如将第 1 步作为第 2 步。我希望能够为落后 1、2 或 3 步分配部分积分。或者对于任何可能正确理解其中大部分内容的人。
如何在 R 中实现这一点? 如果有人能猜出他们是如何完成的,这个链接似乎是一个很好的解决方案:程序化部分信用放入订单分级
谢谢!
人们可能会考虑距离函数,其中“完全正确”的距离为零,而其他任何值都小于 100%。解释这种方法有很大的回旋余地,但任何部分得分而不是“错误!0%”都可以被认为是一种更令人安慰的教学方法。
quux <- structure(list(ResponseID = c("p1", "p2", "p3"), Step1 = c(1L, 2L, 2L), Step2 = c(2L, 3L, 23L), Step3 = c(3L, 4L, 7L), Step22 = c(22L, 23L, 12L), Step23 = c(23L, 1L, 17L)), class = "data.frame", row.names = c(NA, -3L))
quux
# ResponseID Step1 Step2 Step3 Step22 Step23
# 1 p1 1 2 3 22 23
# 2 p2 2 3 4 23 1
# 3 p3 2 23 7 12 17
# you should probably use 1:23, but I only have a subset of data
correct <- c(1, 2, 3, 22, 23)
第一个距离函数严重惩罚(例如)受访者 2 的
Step23
,因为绝对差为 23 - 1 = 22
。数学只是 n 维数据上的毕达哥拉斯数学。我将使用 quux[,-1]
排除 ResponseID
列。
do.call(mapply, c(list(FUN = function(...) sqrt(sum((unlist(list(...)) - correct)^2))), quux[,-1]))
# [1] 0.00000 22.09072 24.37212
这里,
0.000
显然是100%,其他都是各种程度的“不对”。在这个 5 个问题的情况下,最差的分数刚好高于 45.7,其中 Step1=23,Step2=22,...,Step23=1;如果23个都完全落后,那么这里的罚分是
sqrt(sum((1:23 - 23:1)^2))
# [1] 63.62389
如果您认为
22.09072
值得线性部分,如
100 * (63.62389 - do.call(mapply, c(list(FUN = function(...) sqrt(sum((unlist(list(...)) - correct)^2))), quux[,-1]))) / 63.62389
# [1] 100.00000 65.27920 61.69345
### percent
这太仓促了,在接受全权委托之前应该仔细考虑一下。
另一个想法是,你关注的是对/错的存在,而不是距离。也就是说,
Step23=1
的值为 1,Step23=22
的值为 1,但正确的值为 0。
do.call(mapply, c(list(FUN = function(...) sqrt(sum(unlist(list(...)) != correct))), quux[,-1]))
# [1] 0.000000 2.236068 2.236068
请注意,我们在这里不需要
^2
,因为它始终是 0
或 1
,所以正方形不执行任何操作。 (如果它让您对这种方法的毕达哥拉斯性质感觉更好,请随意保留它......)
我们可以轻松地用
==
代替 !=
,并且值将是 100% 的 2.236
,但我认为我会与第一部分中开始的反转保持一致。
完全错误的(所有 23 个都相差 1 或更多)只是
sqrt(sum(2:24 != 1:23))
# [1] 4.795832
您链接的文章从数字来看倾向于“最长的正确排序链”。如果您查看 https://www.r-bloggers.com/2014/09/compute-longest-increasingdecreasing-subsequence-using-rcpp/,这是一种找到数字序列中最长链的方法。
使用它们的功能:
longest_subseq <- function(x) {
P = integer(length(x))
M = integer(length(x) + 1)
L = newL = 0
for (i in seq_along(x) - 1) {
lo = 1
hi = L
while (lo <= hi) {
mid = (lo + hi)%/%2
if (x[M[mid + 1] + 1] < x[i + 1]) {
lo = mid + 1
} else {
hi = mid - 1
}
}
newL = lo
P[i + 1] = M[newL]
if (newL > L) {
M[newL + 1] = i
L = newL
} else if (x[i + 1] < x[M[newL + 1] + 1]) {
M[newL + 1] = i
}
}
k = M[L + 1]
re = integer(L)
for (i in L:1) {
re[i] = k + 1
k = P[k + 1]
}
re
}
然后我们可以找到有多少个数字(每行)的顺序是正确的:
longest_subseq(c(1, 2, 3, 22, 23))
# [1] 1 2 3 4 5 ## length 5, perfect score!
longest_subseq(c(2, 3, 4, 23, 1))
# [1] 1 2 3 4 ## length 4
longest_subseq(c(2, 23, 7, 12, 17))
# [1] 1 3 4 5 ## length 4
并将其自动化为
do.call(mapply, c(list(FUN = function(...) length(longest_subseq(unlist(list(...))))), quux[,-1]))
# [1] 5 4 4
在这种情况下,受访者 1 按正确的顺序拥有所有五个(其中);受访者 2 和 3 只选择了首选相对顺序中的一项。
此方法提供了更清晰的评分:按顺序排列的所有 23 个都是 100%