-
Notifications
You must be signed in to change notification settings - Fork 256
/
chapter_6.Rmd
827 lines (715 loc) · 32.5 KB
/
chapter_6.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
---
title: "Lecture 6"
subtitle: "Data Preprocessing"
author: "Hu Chuan-Peng (PhD)"
institute: "Nanjing Normal University"
date: "2024/04/03"
output:
xaringan::moon_reader:
css: [default, css/Font_Style.css]
lib_dir: libs
nature:
highlightStyle: github
highlightLines: true
countIncrementalSlides: false
---
class: center, middle
<span style="font-size: 60px;">第六讲</span> <br>
<span style="font-size: 50px;">数据预处理</span> <br>
<br>
<br>
<span style="font-size: 30px;">胡传鹏</span> <br>
<span style="font-size: 30px;">2024/04/03</span> <br>
<br>
<br>
<br>
<br>
---
<h1 lang="en" style="font-size: 60px;">Contents</h1>
<br>
<br>
<span style="font-size: 45px;">6.1 Tidyverse</span></center> <br>
<br>
<span style="font-size: 45px;">6.2 问卷数据</span></center> <br>
<span style="font-size: 30px;"> 6.2.1 研究问题 & 数据情况</span></center> <br>
<span style="font-size: 30px;"> 6.2.2 操作步骤</span></center> <br>
<span style="font-size: 30px;"> 6.2.3 小结</span></center> <br>
<br>
<span style="font-size: 45px;">6.3 反应时数据</span></center> <br>
<span style="font-size: 30px;"> 6.3.1 研究问题 & 数据情况</span></center> <br>
<span style="font-size: 30px;"> 6.3.2 操作步骤</span></center> <br>
<span style="font-size: 30px;"> 6.3.3 小结</span></center> <br>
---
# <h1 lang="en">6.1 Tidyverse</h1>
<img src="./picture/chp6/workflow.png" width="85%" style="display: block; margin-left: auto; margin-right: auto;">
- 本课程的数据预处理将基于tidyverse( https://www.tidyverse.org/ ),它是目前最流行的预处理工具,是由8个多功能R包组成的连贯的系统<br>
- 核心包的具体功能与工作流如图所示
---
# <h1 lang="en">6.1 Tidyverse</h1>
<img src="./picture/chp6/cheatsheet.png" width="85%" style="display: block; margin-left: auto; margin-right: auto;">
- 可以通过搜索包的cheatsheet快速了解包的使用<br>
- 官方cheatsheet: https://rstudio.github.io/cheatsheets/
---
# <h1 lang="en">6.1 Tidyverse</h1>
- **优势:共享一个底层设计哲学、语法和数据结构,具有高度的一致性**<br>
<br>
- **"整洁数据(tidy)"**:每行代表一个观察值,每列代表一个变量的值<br>
- **函数的第一个参数总是一个数据框**<br>
- **管道操作符**:连接独立代码,省去中间变量,流水线<br>
最常用的管道操作符为**%>%**,它将一个函数的输出作为下一个函数的输入<br>
<br>
假设需找到`data`中`age`大于`30`的所有行,并排序,代码如下:<br>
```{r eval=FALSE}
filtered_data <- filter(data, age > 30)
filtered_sorted_data <- arrange(filtered_data, age)
```
<br>
使用管道操作符后,代码变为:<br>
```{r eval=FALSE}
filtered_sorted_data <- data %>%
filter(age > 30) %>%
arrange(age)
```
---
# <h1 lang="en">6.1 Tidyverse</h1>
<br><img src="./picture/chp6/pipe.png" width="100%" style="display: block; margin-left: auto; margin-right: auto;">
- tidyverse常见的管道符如表所示,依赖于`magrittr`包<br>
- R 4.1.0 以上版本加入了原生管道操作符`|>`<br>
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;"> 6.2.1 研究问题 & 数据情况</span></center> <br>
<br>
- 课程接下来会以重复[IJzerman et al (2018)](https://doi.org/10.1525/collabra.165)的分析进行问卷数据分析的示例<br>
<br>
- **研究问题**:社交复杂度(CSI)是否影响核心体温(CBT),特别是在离赤道比较远的(低温)地区(DEQ)下<br>
<br>
- **研究假设**:<br>
对于在低温环境中的人来说,(在众多的变量中)社交网络复杂度能够影响个体的核心体温<br>
这一效应受个体的恋爱状态(romantic)调节<br>
<br>
- **研究方法**:路径模型,探索性监督机器学习<br>
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.1 研究问题 & 数据情况</span></center> <br>
- **研究结果**:<br>
<img src="./picture/chp6/pr1.png" width="65%" style="display: block; margin-left: auto; margin-right: auto;">
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.1 研究问题 & 数据情况</span></center> <br>
<br>
- **研究结果**:<br>
<img src="./picture/chp6/pr2.png" width="100%" style="display: block; margin-left: auto; margin-right: auto;">
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.1 研究问题 & 数据情况</span></center><br>
- 数据情况(Hu et al., 2019):<br>
通过data/penguin文件夹下的penguin_full_codebook可以查看详细情况<br>
<img src="./picture/chp6/penguin.png" width="70%" style="display: block; margin-left: auto; margin-right: auto;">
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.1 研究问题 & 数据情况</span></center><br>
- 研究核心变量:<br>
CBT: 核心体温,测量两次,变量为Temperature_t1, Temperature_t2<br>
CSI: 变量为socialdiversity<br>
Site: 数据源站点<br>
DEQ: 距赤道的距离,变量为DEQ<br>
romantic: 是否处于恋爱关系,1 = "yes", 2 = "no"<br>
ALEX: 述情障碍,探索性监督机器学习需要的变量之一,5点量表,变量为ALEX1-16,第4, 12, 14, 16题反向计分
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.1 研究问题 & 数据情况</span></center><br>
```{r}
# 导入数据
df1 <- bruceR::import(here::here('data', 'penguin', 'penguin_rawdata.csv'))
```
```{r example of penguin, echo=FALSE}
DT::datatable(head(df1),
fillContainer = TRUE, options = list(pageLength = 4))
```
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.1 研究问题 & 数据情况</span></center><br>
```{r}
#查看变量名(列名)
colnames(df1)
```
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.2 操作步骤</span></center><br>
- 数据预处理目标:计算感兴趣的变量,按照Site查看被试的平均体温<br>
Step1: 选择变量[select]<br>
Step2: 检查数据类型[glimpse, as族函数]<br>
Step3: 处理缺失值[filter, is.na]<br>
Step4: 计算所需变量[mutate, case when]<br>
Step5: 分组求统计量 [group_by, summarise]
```{r, message=FALSE}
# 不要忘记加载包
library(tidyverse)
```
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.2 操作步骤 | Step1: 选择变量[select]</span></center><br>
```{r}
# 加载包后函数前不需要注明包,此处只是为了提示函数属于哪个包
# 选择我们需要的变量:Temperature_t1, Temperature_t2, SNI28-32, DEQ, romantic, ALEX1-16
df1 <- dplyr::select(df1,
Temperature_t1, Temperature_t2,
socialdiversity,
Site, DEQ,
romantic,
ALEX1:ALEX16)
```
<br>
- select()函数会按照提供的参数顺序选择列<br>
- 可以使用列名、范围(例如 starts_with()、ends_with()、contains()、matches() 等),或者使用 everything() 来选择所有列<br>
- 注意需要将函数结果赋值给一个新的变量/原始变量完成保存
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.2 操作步骤 | Step2: 检查数据类型[summary]</span></center><br>
```{r}
# 检查变量的数据类型
base::summary(df1)
```
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.2 操作步骤 | Step2: 检查数据类型[as族函数]</span></center><br>
```{r}
# 转换数据类型
# 这里数据类型是正确的,只是示例
df1 <- dplyr::mutate(df1,
Temperature_t1_new = as.numeric(Temperature_t1),
Temperature_t2 = as.numeric(Temperature_t2))
```
<br>
- `mutate()`函数常用于创建新的变量或修改现有变量<br>
- 存在多种变式,如`mutate_at()`通过列名、位置或者列的类型进行选择,`mutate_if()`对数据框中满足条件的列应用指定的函数<br>
- `mutate()`内使用函数时,同样需要注意缺失值的问题<br>
- 注意`mutate()`进行转换之后需要进行核查:是否符合预期<br>
- 注意需要将函数结果赋值给一个新的变量/原始变量完成保存
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.2 操作步骤 | Step3: 处理缺失值[filter, is.na]</span></center><br>
```{r}
# 按照Temperature, DEQ处理缺失值
df1 <- filter(df1, !is.na(Temperature_t1)
& !is.na(Temperature_t2) & !is.na(DEQ))
```
<br>
- filter()函数用于从数据框中筛选**行**(观测值),可以通过逻辑运算符组合多个条件<br>
- 运算逻辑:遍历每一行,将给定的条件应用于该行,条件为真则保留,保留的行被组成一个新的数据框作为函数的返回值<br>
- 注意需要将函数结果赋值给一个新的变量/原始变量完成保存
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.2 操作步骤 | Step4: 计算所需变量[mutate]</span></center><br>
```{r}
# 计算每个被试两次核心温度的均值,保存为Temperature
df1 <- dplyr::mutate(df1,
Temperature = rowMeans(select(df1, starts_with("Temperature"))))
```
- `mean()`函数用于计算向量或数组的平均值,`colMeans()`函数用于计算矩阵或数据框的每一列的平均值,`rowMeans()`函数用于计算矩阵或数据框的每一行的平均值<br>
- 数据类型需为numeric<br>
- `starts_with()`用于在数据框中选择列名以特定字符串开头的列
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.2 操作步骤 | Step4: 计算所需变量[mutate, case when]</span></center><br>
```{r}
# 将4, 12, 14, 16题反向计分,计算ALEX,保存为ALEX
df1 <- mutate(df1,
ALEX4 = case_when(
TRUE ~ 6 - ALEX4 # 反向计分:6减去原始值
),
ALEX12 = case_when(TRUE ~ 6 - ALEX12),
ALEX14 = case_when(TRUE ~ 6 - ALEX14),
ALEX16 = case_when(TRUE ~ 6 - ALEX16)
)
#也可以写成 case_when(ALEX4 == '1' ~ '5',ALEX4 == '2' ~ '4', ALEX4 == '3' ~ '3', ALEX4 == '4' ~ '2', ALEX4 == '5' ~ '1',TRUE ~ as.character(ALEX4))
```
- `case_when()`函数是一个强大的条件判断函数,通常用于根据不同的条件生成新的变量或对现有变量进行转换<br>
- 运算逻辑:逐行评估每个条件,并根据条件的结果来确定新值,若条件为真,则用‘~’后的值替换原始值<br>
- 有多个条件时,按照条件的顺序逐个进行判断,一旦有条件满足,则返回对应的值并停止继续判断其他条件<br>
- 使用 `TRUE ~` 或者 `TRUE ~ NA`处理未匹配到任何条件的情况,这样可以确保即使所有条件都不满足时,函数也会返回一个默认值,避免产生错误
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.2 操作步骤 | Step4: 计算所需变量[mutate, case when]</span></center><br>
- **前后对比**:<br>
<img src="./picture/chp6/contrast.png" width="100%" style="display: block; margin-left: auto; margin-right: auto;">
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.2 操作步骤 | Step4: 计算所需变量[mutate, case when]</span></center><br>
- 使用case when()要确保条件中使用的值与变量的数据类型匹配<br>
```{r, eval=FALSE}
# age为num
case_when(
age < 18 ~ "Child",
age >= 18 & age < 65 ~ "Adult",
age >= 65 ~ "Senior"
)
# age为chr
case_when(
age < "18" ~ "Child",
age >= "18" & age < "65" ~ "Adult",
age >= "65" ~ "Senior"
)
```
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.2 操作步骤 | Step4: 计算所需变量[mutate]</span></center><br>
```{r}
# 计算ALEX
df1 <- mutate(df1,
ALEX = rowSums(select(df1, starts_with("ALEX"))))
# 查看数据
df1
```
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.2 操作步骤 | Step4: 计算所需变量[bruceR]</span></center><br>
- bruceR中的反向计分并计算变量的等效操作
```{r, eval=FALSE}
# 创建一个包含需要反向计分的变量的列表
vars_to_reverse <- c("ALEX4", "ALEX12", "ALEX14", "ALEX16")
# 对列表中的变量进行反向计分
df1$ALEX <- bruceR::SUM(df1,
varrange = "ALEX1:ALEX16",
rev = vars_to_reverse,
likert = 1:5)
```
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.2 操作步骤 | Step5: 分组描述[group_by, summarise]</span></center><br>
```{r}
# 按Site计算Temperature的平均值
df1 <- dplyr::group_by(df1, Site)
df2 <- dplyr::summarise(df1, mean_Temperature = mean(Temperature), n = n())
df1 <- dplyr::ungroup(df1)
```
<br>
- group_by()函数将数据框按照指定的分组变量进行分组,然后可以对每个分组进行单独的操作,如汇总、计算统计量等<br>
- 在完成分组操作后,建议使用ungroup()函数取消数据框的分组状态<br>
- summarise()函数用于对数据框进行汇总操作,常与group_by()连用<br>
- 在summarise()函数中,可以使用各种统计函数来计算汇总统计量,例如n()、mean()、sum()、median()、min()、max()、sd()等
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.2 操作步骤 | 完整的管道操作</span></center><br>
```{r}
# 用管道操作符合并以上代码
# 使用管道操作符时建议先单独查看变量的数据类型,转换完毕后在进行操作
# dplyr::glimpse(penguin_data)
df2 <- df1 %>%
dplyr::select(Temperature_t1, Temperature_t2, socialdiversity, Site, DEQ, romantic, ALEX1:ALEX16) %>%
dplyr::filter(!is.na(Temperature_t1) & !is.na(Temperature_t2) & !is.na(DEQ)) %>%
dplyr::mutate(Temperature = rowMeans(select(., starts_with("Temperature"))),
ALEX4 = case_when(TRUE ~ 6 - ALEX4),
ALEX12 = case_when(TRUE ~ 6 - ALEX12),
ALEX14 = case_when(TRUE ~ 6 - ALEX14),
ALEX16 = case_when(TRUE ~ 6 - ALEX16),
ALEX = rowSums(select(., starts_with("ALEX")))) %>%
dplyr::group_by(Site) %>%
dplyr::summarise(mean_Temperature = mean(Temperature)) %>%
dplyr::ungroup()
# 查看数据
df2
```
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.3 小结</span></center><br>
_数据的预处理主要依赖`dplyr`包,常见函数总结如下_
- filter() 选择符合某个条件的行(可能代表一个被试的数据) <br>
- mutate() 创建新的变量或修改现有变量 <br>
- case when() 重新编码变量<br>
- group_by() 依据某些变量产生的条件,给数据分组 <br>
**如果使用 "group_by",** <br>
**一定要在summarise后使用 "ungroup".** <br>
- summarise() 进行某些加减乘除的运算 <br>
- ungroup() 取消刚刚进行的分组 <br>
- select() 选择进行分析时需要用到的变量,同时也起到了为所有变量排序的功能 <br>
- arrange() 某一列的值,按照某个顺序排列(其他列也会随之变动) <br>
---
# <h1 lang="en">6.2 问卷数据</h1>
<span style="font-size: 30px;">6.2.3 小结</span></center><br>
<br>
- 练习<br>
1. 分步骤使用bruceR计算ALEX的值,保留ALEX在30-50间的被试,按照langfamily进行分组,计算Temperature均值<br>
<br>
2. 使用管道操作符合并上述代码<br>
<br>
3. 按照langfamily进行分组计算DEQ, CSI的均值
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.1 研究问题 & 数据情况</span></center> <br>
- 课程将重复[Hu et al.,2020](https://doi.org/10.1525/collabra.301)作为反应时数据分析的示例<br>
<br>
- **研究问题**:探究人们对自我相关刺激的优先加工是否仅在某些条件下发生<br>
- **研究假设**:无论参与何种任务,与积极概念(好我)建立联结的自我形状会在反应时间和准确性上表现更快更准确<br>
<img src="./picture/chp6/match.png" width="70%" style="display: block; margin-left: auto; margin-right: auto;">
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.1 研究问题 & 数据情况</span></center> <br>
- **研究结果**:<br>
<br>
<img src="./picture/chp6/mr.png" width="100%" style="display: block; margin-left: auto; margin-right: auto;">
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.1 研究问题 & 数据情况</span></center> <br>
- 数据情况:<br>
数据保存于data/match文件夹下<br>
N(被试) = 44, N(files) = 44<br>
形状标签匹配任务数据命名为`data_exp7_rep_match_*.out`<br>
<br>
<img src="./picture/chp6/matchdata.png" width="100%" style="display: block; margin-left: auto; margin-right: auto;">
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.1 研究问题 & 数据情况</span></center> <br>
- 主要变量:<br>
Shape/Label: 屏幕呈现的图形代表的概念<br>
Match: 图形与呈现的标签是否匹配<br>
ACC: 被试的判断是否正确,1 = "正确", 0 = "错误", -1, 2表示未按键或按了两个键的情况,属于无效作答<br>
RT: 被试做出判断的反应时,[200,1500]的反应时纳入分析<br>
```{r example of singal rawdata_matchtask DT, echo=FALSE}
a1 <- utils::read.table("data/match/data_exp7_rep_match_7302.out", header = TRUE)
DT::datatable(head(a1),
fillContainer = TRUE, options = list(pageLength = 4))
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤</span></center><br>
- 数据预处理目标:计算实验条件为Match-Moral时RT的SPE。<br>
**Step1: 批量读取并合并数据[for loop]**<br>
Step2: 选择变量[select]<br>
Step3: 处理缺失值[drop_na, filter]<br>
Step4: 分实验条件计算变量[group_by, summarise]<br>
**Step5: 拆分变量[extract, filter]**<br>
**Step6: 将长数据转为宽数据[pivot_wide]**<br>
Step7: 计算实验条件为Match-Moral时RT的自我优势效应[mutate, select]<br>
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step1: 批量读取并合并数据[for loop]</span></center><br>
- 在选用读取数据的函数时要注意函数默认的分隔符(参数sep),如read.csv默认为",", read.table默认为" "<br>
- .out文件是以空格或制表符分隔的文本文件
```{r}
# 查看单个被试的数据
# 查看数据时要注意所需变量的数据类型,如果存在问题需要提前转换
p1 <- utils::read.table("data/match/data_exp7_rep_match_7302.out", header = TRUE)
p2 <- utils::read.table("data/match/data_exp7_rep_match_7303.out", header = TRUE)
p1
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step1: 批量读取并合并数据[for loop]</span></center><br>
```{r}
# 将两个被试的数据合并
df3 <- base::rbind(p1, p2)
df3 <- dplyr::bind_rows(p1, p2)
```
- rbind()函数,用于合并两个或多个数据框、矩阵、数组或列表,并将它们按行连接成一个新的对象,其中的 "r" 代表 "row"。<br>
- 通常情况下,rbind会返回一个矩阵,但输入对象的特性(如列名、列属性等)也可能导致rbind()返回数据框。想要获得数据框最好使用bind_rows()函数。<br>
- 合并数据框时,要确保被合并的数据框具有相同的列数和列名。如果列名不同,bind_rows()会尝试按照列名的顺序进行合并。如果无法自动匹配列名,则会产生错误。
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step1: 批量读取并合并数据[for loop]</span></center><br>
- 虽然可以通过逐个导入数据并逐个合并得到最终的数据,但这样费时费力,且代码冗余。<br>
- 编程中常用迭代结构执行重复操作,如for loop。<br>
- for loop的基本语法如下:for (variable in sequence) { # 在这里执行循环体操作 }
```{r}
# 单个操作循环,打印i + 1
for (i in 1:10) {
print(i + 1)
}
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step1: 批量读取并合并数据[for loop]</span></center><br>
<img src="./picture/chp6/loop.png" width="60%" style="display: block; margin-left: auto; margin-right: auto;">
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step1: 批量读取并合并数据[for loop]</span></center><br>
```{r}
# variable in sequence
for (i in 1:5) {print(i)}
for (i in seq(1, 5)) {print(i) }
my_vector <- c(1, 2, 3, 4, 5)
for (i in my_vector) {print(i) }
my_list <- list(a = 1, b = 2, c = 3)
for (element in my_list) {print(element) }
my_string <- "world"
for (i in 1:nchar(my_string)) {print(substr(my_string, i, i)) }
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step1: 批量读取并合并数据[for loop]</span></center><br>
```{r}
# variable in sequence
my_list <- list(a = 1, b = 2, c = 3)
for (element in my_list) {print(element)}
my_string <- "world"
for (i in 1:nchar(my_string)) {print(substr(my_string, i, i))}
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step1: 批量读取并合并数据[for loop]</span></center><br>
```{r}
# 加简单条件
for (i in 1:10) {
if (i > 5) {
print(i + 1)
}
}
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step1: 批量读取并合并数据[for loop]</span></center><br>
- 那么要如何利用for loop批量导入数据呢?<br>
```{r for loop list.files, error=FALSE}
# 把所有符合某种标题的文件全部读取到一个list中
# 使用 full.names 参数获取完整路径的文件列表
files <- list.files(here::here("data", "match"), pattern = "data_exp7_rep_match_.*\\.out$", full.names = TRUE)
head(files, n = 10L)
str(files)
```
*P.S.尽管函数叫list.files,但它得到的变量的属性是value,而不是list*
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step1: 批量读取并合并数据[for loop]</span></center><br>
```{r df.mt.out.fl}
# 定义函数用于数据类型转换
convert_data_types = function(df) {
df <- df %>%
dplyr::mutate(Date = as.character(Date),
Prac = as.character(Prac),
Sub = as.numeric(Sub),
Age = as.numeric(Age),
Sex = as.character(Sex),
Hand = as.character(Hand),
Block = as.numeric(Block),
Bin = as.numeric(Bin),
Trial = as.numeric(Trial),
Shape = as.character(Shape),
Label = as.character(Label),
Match = as.character(Match),
CorrResp = as.character(CorrResp),
Resp = as.character(Resp),
ACC = as.numeric(ACC),
RT = as.numeric(RT))
return(df)
}
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step1: 批量读取并合并数据[for loop]</span></center><br>
```{r}
# 创建一个空的数据框来存储读取的数据
df3 <- data.frame()
# 循环读取每个文件,处理数据并添加到数据框中
for (i in seq_along(files)) { # 重复"读取到的.out个数"的次数
# 读取数据文件
df <- read.table(files[i], header = TRUE)
# 使用 filter 函数过滤掉 Date 列值为 "Date" 的行
df <- dplyr::filter(df, Date != "Date")
# 调用函数进行数据类型转换
df <- convert_data_types(df)
# 使用 bind_rows() 函数将当前数据框与之前的数据框合并
df3 <- dplyr::bind_rows(df3, df)
}
# rbind合并后是matrics,需要转换
# df3 <- as.data.frame(do.call(rbind, df_list))
# 清除中间变量
rm(df, files, i)
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step1: 批量读取并合并数据[for loop]</span></center><br>
- 使用lapply也能完成批量导入与合并。lapply思维难度更高,但代码更简洁。<br>
```{r error=FALSE}
# 获取所有的.out文件名
files <- list.files(here::here("data", "match"), pattern = "data_exp7_rep_match_.*\\.out$", full.names = TRUE)
# 读取每个.out文件,并进行数据清洗
df3 <- lapply(files, function(file) {
df <- read.table(file, header = TRUE)
df <- dplyr::filter(df, Date != "Date") # 过滤掉 Date 列值为 "Date" 的行
df <- mutate(df,
convert_data_types(df)
) # 进行数据类型转换和数据清洗
return(df)
})
# 合并所有数据框
df3 <- dplyr::bind_rows(df3)
# 清除中间变量
rm(files)
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step1: 批量读取并合并数据[for loop]</span></center><br>
- 保存合并的数据文件。<br>
```{r error=FALSE}
#for loop 或 lapply的都可以
write.csv(df3,
file = here::here("data", "match","match_raw.csv"),
row.names = FALSE)
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step2: 选择变量[select]</span></center><br>
```{r example of total part1 rawdata_matchtask,message=FALSE}
# 选择我们需要的变量
df3 <- dplyr::select(df3,
Sub, Age, Sex, Hand, #人口统计学
Block, Bin, Trial, # 试次
Shape, Label, Match, # 刺激
Resp, ACC, RT) # 反应结果
```
```{r example of total part1 rawdata_matchtask DT, echo=FALSE}
DT::datatable(head(df3, 24),
fillContainer = TRUE, options = list(pageLength = 5))
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step3: 处理缺失值[drop_na, filter]</span></center><br>
```{r example of total part2 rawdata_matchtask,message=FALSE}
# 删除缺失值,选择符合标准的被试
df4 <- tidyr::drop_na(df3) # 删除含有缺失值的行
df4 <- dplyr::filter(df3, Hand == "R", # 选择右利手被试
ACC == 0 | ACC == 1 , # 排除无效应答(ACC = -1 OR 2)
RT >= 0.2 & RT <= 1.5) # 选择RT属于[200,1500]
```
```{r example of total part2 rawdata_matchtask DT, echo=FALSE}
DT::datatable(head(df4, 24),
fillContainer = TRUE, options = list(pageLength = 5))
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step4: 分条件描述[group_by, summarise]</span></center><br>
```{r example of total part3 rawdata_matchtask,message=FALSE}
# 分实验条件计算
df4 <- dplyr::group_by(df4, Sub, Shape, Label, Match)
df4 <- dplyr::summarise(df4, mean_ACC = mean(ACC), mean_RT = mean(RT))
df4 <- dplyr::ungroup(df4)
```
```{r example of total part3 rawdata_matchtask DT, echo=FALSE}
DT::datatable(head(df4, 24),
fillContainer = TRUE, options = list(pageLength = 5))
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step5: 拆分变量[extract, filter]</span></center><br>
```{r example of total part4 rawdata_matchtask}
# 将Shape变量拆分
df4 <- tidyr::extract(df4, Shape, into = c("Valence", "Identity"),
regex = "(moral|immoral)(Self|Other)", remove = FALSE)
df4 <- dplyr::filter(df4, Match == "match" & Valence == "moral")
```
```{r example of total part4 rawdata_matchtask DT, echo=FALSE}
DT::datatable(head(df4, 24),
fillContainer = TRUE, options = list(pageLength = 5))
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤 | Step6: 将数据长转宽[pivot_wide]</span></center><br>
```{r example of total part5 rawdata_matchtask}
# 将长数据转为宽数据
df4 <- dplyr::select(df4, Sub, Identity, mean_RT)
df4 <- tidyr::pivot_wider(df4, names_from = "Identity", values_from = "mean_RT")
```
```{r example of total part5 rawdata_matchtask DT, echo=FALSE}
DT::datatable(head(df4, 24),
fillContainer = TRUE, options = list(pageLength = 5))
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤</span></center><br>
```{r example of total part6 rawdata_matchtask}
# 计算SPE
df4 <- dplyr::mutate(df4, moral_SPE = Self - Other)
df4 <- dplyr::select(df4, Sub, moral_SPE)
```
```{r example of total part6 rawdata_matchtask DT, echo=FALSE}
DT::datatable(head(df4, 24),
fillContainer = TRUE, options = list(pageLength = 5))
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.2 操作步骤</span></center><br>
```{r example of total rawdata_matchtask, message=FALSE}
# 用管道操作符合并以上代码
df4 <- df3 %>%
dplyr::select(Sub, Age, Sex, Hand, #人口统计学
Block, Bin, Trial, # 试次
Shape, Label, Match, # 刺激
Resp, ACC, RT, # 反应结果
) %>%
tidyr::drop_na() %>% #删除缺失值
dplyr::filter(.,Hand == "R", # 选择右利手被试
ACC == 0 | ACC == 1 , # 排除无效应答(ACC = -1 OR 2)
RT >= 0.2 & RT <= 1.5 # 选择RT属于[200,1500]
) %>%
dplyr::group_by(Sub,
Shape, Label, Match) %>%
dplyr::summarise(mean_ACC = mean(ACC),
mean_RT = mean(RT)) %>%
dplyr::ungroup() %>%
tidyr::extract(Shape, into = c("Valence", "Identity"),
regex = "(moral|immoral)(Self|Other)", remove = FALSE) %>%
dplyr::filter(Match == "match" & Valence == "moral") %>%
dplyr::select(Sub, Identity, mean_RT) %>%
tidyr::pivot_wider(names_from = "Identity", values_from = "mean_RT") %>%
dplyr::mutate(moral_SPE = Self - Other) %>%
dplyr::select(Sub, moral_SPE)
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.3 小结</span></center><br>
<br>
- separate() 把一个变量的单元格内的字符串拆成两份,变成两个变量 <br>
**更适合用于按固定分隔符分割字符串,如将“2022-02-25”分成“2022”、“02”和“25”三列** <br>
- extract() 类似于separate <br>
**更适合用于从字符串中提取特定的信息,如将“John Smith”分成“John”和“Smith”两列** <br>
- unite() 把多个列(字符串)整合为一列 <br>
- pivot_longer() 把宽数据转化为长数据 <br>
- pivot_wider() 把长数据转化为宽数据 <br>
- drop_na() 删除缺失值
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.3 小结</span></center><br>
- 练习<br>
计算不同Shape情况下(immoralself,moralself,immoralother,moralother)<br>
基于信号检测论match与mismatch之间的d'(match为信号,mismatch噪音)<br>
以下是计算信号检测论d'的代码 <br>
```{r, eval=FALSE}
dplyr::summarise(
hit = length(ACC[Match == "match" & ACC == 1]),
fa = length(ACC[Match == "mismatch" & ACC == 0]),
miss = length(ACC[Match == "match" & ACC == 0]),
cr = length(ACC[Match == "mismatch" & ACC == 1]),
Dprime = qnorm(
ifelse(hit / (hit + miss) < 1,
hit / (hit + miss),
1 - 1 / (2 * (hit + miss))
)
)
- qnorm(
ifelse(fa / (fa + cr) > 0,
fa / (fa + cr),
1 / (2 * (fa + cr))
)
)
)
```
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.3 小结</span></center><br>
- **练习思路**<br>
<br>
Step1: 选择需要的变量 <br>
<br>
Step2: 基于Sub, Shape分组 <br>
<br>
Step3: 使用计算公式 <br>
<br>
Step4: 删除击中、虚报、误报、正确拒绝 <br>
<br>
Step5: 长转宽,得到每个Shape情况下的信号检测论d值 <br>
---
# <h1 lang="en">6.3 反应时数据</h1>
<span style="font-size: 30px;">6.3.3 小结</span></center><br>
- **答案参考**<br>
<br>
<img src="./picture/chp6/answer.png" width="100%" style="display: block; margin-left: auto; margin-right: auto;">