6  Återhämtning

Code
library(foreign)
library(readxl)
library(RISEkbmRasch) # devtools::install_github("pgmj/RISEkbmRasch")
library(grateful) # devtools::install_github("Pakillo/grateful")
library(ggrepel)
library(car)
library(kableExtra)
library(readxl)
library(tidyverse)
library(eRm)
library(mirt)
library(psych)
library(ggplot2)
library(psychotree)
library(matrixStats)
library(reshape)
library(knitr)
library(cowplot)
library(formattable) 
library(glue)
library(hexbin)
library(skimr)


### some commands exist in multiple packages, here we define preferred ones that are frequently used
select <- dplyr::select
count <- dplyr::count
recode <- car::recode
rename <- dplyr::rename

# file paths will need to have "../" added at the beginning to be able to render document

# get itemlabels
itemlabels <- read_excel("../data/Itemlabels.xlsx") %>% 
  filter(str_detect(itemnr, pattern = "å")) %>% 
  select(!Dimension)

spssDatafil <- "../data/2023-04-26 Prevent OSA-enkat.sav"

# read SurveyMonkey data
df <- read.spss(spssDatafil, to.data.frame = TRUE) %>% 
  select(starts_with("q0009"),q0001,q0002,q0003,q0004) %>% 
  rename(Kön = q0002,
         Ålder = q0001,
         Bransch = q0003,
         Hemarbete = q0004)



# SPSS format provides itemlabels too, we can save them in a dataframe
spssLabels <- df %>% attr('variable.labels') %>% as.data.frame()

dif.kön <- df$Kön
dif.ålder <- df$Ålder
dif.bransch <- df$Bransch
dif.hemarbete <- df$Hemarbete

df <- df %>% 
  select(starts_with("q0009"))

names(df) <- itemlabels$itemnr

6.1 Items

Code
itemlabels %>% 
  kbl_rise(width = 60)
itemnr item
å1 Jag kan använda raster till att koppla av från arbetet.
å2 Jag har möjlighet att arbeta i lugnare takt efter arbetsintensiva perioder.
å3 Jag har tid för reflektion över hur jag har utfört arbetet.
å4 Jag har ork kvar för att göra andra saker efter arbetsdagens slut.
å5 Jag kan lägga tankar på arbetet åt sidan på min lediga tid.

6.2 Bakgrundsdata

Code
RIdemographics(dif.kön,"Kön")
Kön n Percent
Kvinna 402 69.7
Man 172 29.8
Annat 1 0.2
Vill ej uppge 2 0.3
Code
RIdemographics(dif.ålder,"Ålder") # kanske även fixa en figur?
Ålder n Percent
18-29 22 3.8
30-39 103 17.8
40-49 175 30.2
50-59 215 37.1
60+ 64 11.1
Code
RIdemographics(dif.bransch,"Bransch")
Bransch n Percent
Kontorsarbete (oavsett bransch) 284 49.1
Industri 26 4.5
Hotell, restaurang, service 8 1.4
Handel 16 2.8
Skola, utbildning 67 11.6
Vård, omsorg 99 17.1
Byggverksamhet 7 1.2
Annat 71 12.3
Code
RIdemographics(dif.hemarbete,"Antal dagar med arbete hemifrån")
Antal dagar med arbete hemifrån n Percent
En dag 136 23.5
Två dagar 105 18.1
Tre dagar 35 6.0
Fyra dagar 10 1.7
Fem dagar 2 0.3
Jag arbetar aldrig eller sällan hemifrån 291 50.3

6.2.1 Svarsbortfall items

Vi filtrerar bort respondenter som har färre än två svar på frågorna i delskalan.

Code
# If you want to include participants with missing data, input the minimum number of items responses that a participant should have to be included in the analysis:
min.responses <- 2
scale.items <- itemlabels$itemnr

# Select the variables we will work with, and filter out respondents with a lot of missing data
df.omit.na <- df %>% 
  filter(length(scale.items)-rowSums(is.na(.[scale.items])) >= min.responses)

RImissing(df.omit.na,"å")

Vi har extremt få saknade svar, och tar därför bort respondenterna som inte har kompletta svar.

Code
df <- read.spss(spssDatafil, to.data.frame = TRUE) %>% 
  select(starts_with("q0009"),q0001,q0002,q0003,q0004) %>% 
  rename(Kön = q0002,
         Ålder = q0001,
         Bransch = q0003,
         Hemarbete = q0004) %>% 
  na.omit()

dif.kön <- df$Kön
dif.ålder <- df$Ålder
dif.bransch <- df$Bransch
dif.hemarbete <- df$Hemarbete

df <- df %>% 
  select(starts_with("q0009"))

names(df) <- itemlabels$itemnr

df.omit.na <- df

6.2.2 Alla svar för alla frågor

Code
RIallresp(df.omit.na)
Response category Number of responses Percent
Alltid 261 10.7
Mycket ofta 562 23.0
Ganska ofta 624 25.6
Ibland 538 22.0
Sällan 363 14.9
Aldrig 92 3.8
Code
# koda om svarskategorier till siffror
df.omit.na <- df.omit.na %>% 
  mutate(across(everything(), ~ car::recode(.x,"'Aldrig'=0;
                                            'Sällan' =1;
                                            'Ibland'=2;
                                            'Ganska ofta'=3;
                                            'Mycket ofta'=4;
                                            'Alltid'=5",
                                            as.factor = FALSE)))

6.2.3 Descriptives - all items

Code
RItileplot(df.omit.na)

Code
RIbarstack(df.omit.na) + ylab("Item")

Code
RIbarplot(df.omit.na)

Vi har tillräckligt med svar i samtliga svarskategorier och fortsätter därför till analysen utan omkodning.

6.3 Rasch-analys 1

itemnr item
å1 Jag kan använda raster till att koppla av från arbetet.
å2 Jag har möjlighet att arbeta i lugnare takt efter arbetsintensiva perioder.
å3 Jag har tid för reflektion över hur jag har utfört arbetet.
å4 Jag har ork kvar för att göra andra saker efter arbetsdagens slut.
å5 Jag kan lägga tankar på arbetet åt sidan på min lediga tid.
Code
RIitemfitPCM2(df.omit.na, samplesize = 250, nsamples = 32, cpu = 8)
OutfitMSQ InfitMSQ OutfitZSTD InfitZSTD
å1 0.82 0.814 -2.173 -2.133
å2 0.669 0.673 -4.089 -4.187
å3 0.665 0.676 -4.351 -4.176
å4 0.82 0.813 -2.206 -2.185
å5 1.172 1.129 1.902 1.392
Code
RIpcmPCA(na.omit(df.omit.na))
PCA of Rasch model residuals
Eigenvalues
1.82
1.34
1.01
0.82
0.00
Code
RIresidcorr(df.omit.na, cutoff = 0.2)
å1 å2 å3 å4 å5
å1
å2 -0.15
å3 -0.2 0.21
å4 -0.34 -0.26 -0.21
å5 -0.25 -0.45 -0.45 -0.09
Note:
Relative cut-off value (highlighted in red) is -0.019, which is 0.2 above the average correlation.
Code
RIloadLoc(df.omit.na)

Code
RIitemCats(df.omit.na, items = "all")

Code
# increase fig-height above as needed, if you have many items
RItargeting(df.omit.na, xlim = c(-5,5))

Code
RIitemHierarchy(df.omit.na)

Överlag ser analysens utfall bra ut (förutom något låg item fit för å2 och å3). Dock finns en stor residualkorrelation mellan å2 och å3. Båda har något låg item fit och utifrån targeting tar vi bort item å2, eftersom svarströskel 4 (T4) för å3 fyller ut en region där vi annars saknar svarströsklar.

Code
# create vector with eliminated items
removed_items <- c("å2")

# select all items except those removed
df2 <- df.omit.na %>%
  select(!all_of(removed_items))

6.4 Rasch-analys 2

itemnr item
å1 Jag kan använda raster till att koppla av från arbetet.
å3 Jag har tid för reflektion över hur jag har utfört arbetet.
å4 Jag har ork kvar för att göra andra saker efter arbetsdagens slut.
å5 Jag kan lägga tankar på arbetet åt sidan på min lediga tid.
Code
RIitemfitPCM2(df2, samplesize = 250, nsamples = 32, cpu = 8)
OutfitMSQ InfitMSQ OutfitZSTD InfitZSTD
å1 0.758 0.748 -2.788 -2.862
å3 0.718 0.727 -3.511 -3.51
å4 0.703 0.706 -3.818 -3.777
å5 0.932 0.922 -0.806 -0.896
Code
RIpcmPCA(na.omit(df2))
PCA of Rasch model residuals
Eigenvalues
1.59
1.39
1.02
0.00
Code
RIresidcorr(df2, cutoff = 0.2)
å1 å3 å4 å5
å1
å3 -0.13
å4 -0.39 -0.16
å5 -0.35 -0.47 -0.21
Note:
Relative cut-off value (highlighted in red) is -0.084, which is 0.2 above the average correlation.
Code
RIloadLoc(df2)

Code
# increase fig-height above as needed, if you have many items
RItargeting(df2)

Code
RIitemHierarchy(df2)

6.4.1 Targeting

Code
# increase fig-height above as needed, if you have many items
RItargeting(df2)

Figur 6.1: Targeting för området Återhämtning

Förutom en något låg item fit för å1, å3 och å4 (zstd) ser analysens utfall bra ut. Vi går vidare till att undersöka DIF och reliabilitet.

6.5 DIF-analysis

En DIF-analys bör inte inkludera svarskategorier med för få svar. Därför har svarskategorier med färre än 50 svar antingen exkluderas eller slagits ihop med andra svarskategorier (se avsnittet om bakgrundsdata för exakt antal svar per ordinarie kategori). DIF-variablerna omkodades till att innehålla följande kategorier:

  • Kön: man, kvinna
  • Ålder: 30-39, 40-49, 50-59, 60+
  • Bransch: kontorsarbete, ej kontorsarbete
  • Hemarbete: aldrig eller sällan, en dag, minst två dagar

Koden nedan specificerar exakt hur omkodningen gick till.

Code
# Omkodning för kön
dif.kön <- recode(dif.kön,"'Annat'=NA;'Vill ej uppge'=NA")

# Omkodning för ålder
dif.ålder <- recode(dif.ålder,"'18-29'=NA")

# Omkodning för bransch
dif.bransch <- recode(dif.bransch,"'Industri'='Ej kontorsarbete';'Hotell, restaurang, service'='Ej kontorsarbete';'Handel'='Ej kontorsarbete';'Skola, utbildning'='Ej kontorsarbete';'Vård, omsorg'='Ej kontorsarbete';'Byggverksamhet'='Ej kontorsarbete';'Annat'='Ej kontorsarbete'")

#Omkodning för hemarbete
dif.hemarbete <- recode(dif.hemarbete,"'Fem dagar'='Minst två dagar';'Fyra dagar'='Minst två dagar';'Tre dagar'='Minst två dagar';'Två dagar'='Minst två dagar'")
Code
RIdifTable(df2, dif.kön)
[1] "No significant DIF found."
Code
RIdifTable(df2, dif.ålder)
[1] "No significant DIF found."
Code
RIdifTable(df2, dif.bransch)
[1] "No significant DIF found."
Code
RIdifTable(df2, dif.hemarbete)
[1] "No significant DIF found."

Inga problem med DIF baserat på kön, ålder, bransch och hemarbete.

6.6 Reliability

Code
RItif(df2, lo = -5, hi = 5)

Figur 6.2: Reliabilitet för området Återhämtning

Reliabiliteten kommer upp i önskade nivåer på vissa delar av skalan. Vi går vidare och tar fram item-parametrar och en konverteringstabell.

6.7 Person fit

Code
RIpfit(df2)

6.8 Item-parametrar

Code
RIitemparams(df2)
Threshold 1 Threshold 2 Threshold 3 Threshold 4 Threshold 5 Item location
å1 -1.73 0.22 0.89 1.33 3.19 0.78
å3 -2.11 0.11 1.02 2.36 3.21 0.92
å4 -1.97 -0.24 0.66 1.76 3.19 0.68
å5 -1.57 -0.15 0.86 1.50 3.37 0.8
Code
#RIitemparams(df2, "itemParams_rec.csv")

6.9 Transformeringstabell

Code
RIscoreSE(df2, width = 50, sdx = 10, score_range = c(-3,4))
Ordinal sum score Logit score Logit std.error
0 -3.000 1.069
2 -2.014 0.808
3 -1.458 0.719
4 -0.989 0.658
5 -0.587 0.611
6 -0.240 0.574
7 0.064 0.545
8 0.339 0.526
9 0.595 0.514
10 0.842 0.509
11 1.088 0.512
12 1.341 0.521
13 1.611 0.537
14 1.905 0.560
15 2.230 0.589
16 2.593 0.627
17 3.003 0.680
18 3.491 0.766

6.9.1 Estimering av mätvärden

Code
df2$score <- RIestThetas(df2, theta_range = c(-3,4))
write.csv(df2, "scored.csv")
summary(df2$score)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-3.0000  0.3393  1.0880  1.2141  2.2301  4.0000 
Code
#df2 <- read_csv("05_rec/scored.csv")

ggplot(
  data = df2,
  aes(x = score, y = 0, fill = factor(score))
  ) +
  geom_dotplot(color = "white", 
                dotsize = 1,
                binwidth = 0.12) +
   # plot mean value
  geom_point(
    aes(
      x = mean(score),
      y = 0
    ),
    size = 6,
    shape = 18,
    color = "black"
  ) +
    # errorbar has brackets at the endpoints
  geom_errorbar(
    aes(
      xmin = mean(score) - sd(score),
      xmax = mean(score) + sd(score),
      y = 0,
      width = 0.025
    ),
    color = "black"
  ) +
  theme_minimal(base_family = "Lato") +
  theme_rise() +
  coord_cartesian(xlim = c(-3,4)) +
  scale_color_viridis_d("Antal svar",
    begin = 0.2,
    aesthetics = c("fill", "color"),
    guide = "none"
  ) +
  labs(
    title = "Mätvärden",
    subtitle = "Värden längre till höger är bättre",
    caption = "Svart diamant indikerar medelvärde, strecken runt indikerar en standardavvikelse.",
    y = "Antal respondenter",
    x = "Mätvärden"
  ) +
  theme(
    #axis.text.x = element_blank(), # remove text from both axes
    axis.text.y = element_blank()
  )

6.10 Rekommenderade items

Code
itemlabels %>% 
  filter(itemnr %in% names(df2)) %>% 
  write_csv("finalItems.csv")

itemlabels %>% 
  filter(!itemnr %in% names(df2)) %>% 
  write_csv("removedItems.csv")

itemlabels %>% 
  kbl_rise(width = 50,
           options = "hover") %>% 
  row_spec(c(2), background = "lightpink") %>% 
  footnote(general_title = "Notera: ",
           "Borttagna item markerade med färg.")
itemnr item
å1 Jag kan använda raster till att koppla av från arbetet.
å2 Jag har möjlighet att arbeta i lugnare takt efter arbetsintensiva perioder.
å3 Jag har tid för reflektion över hur jag har utfört arbetet.
å4 Jag har ork kvar för att göra andra saker efter arbetsdagens slut.
å5 Jag kan lägga tankar på arbetet åt sidan på min lediga tid.
Notera:
Borttagna item markerade med färg.

6.10.1 Förändringar i svarskategorier

Inga.