2  Arbetets organisation

Code
library(foreign)
library(readxl)
library(RISEkbmRasch) # devtools::install_github("pgmj/RISEkbmRasch")
library(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

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

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

# read SurveyMonkey data
df <- read.spss(spssDatafil, to.data.frame = TRUE) %>% 
  select(starts_with("q0005"),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("q0005"))

names(df) <- itemlabels$itemnr

2.1 Items

Code
kbl_rise(itemlabels, options = "hover", width = 45)
itemnr item
ao1 Jag vet vilka arbetsuppgifter jag har.
ao2 Jag vet hur mitt arbete ska utföras.
ao3 Jag vet vilka resultat som jag ska uppnå med mitt arbete.
ao4 Det finns tydliga mål för min arbetsgrupp.
ao5 Det finns tydliga mål för min organisation.

2.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

2.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,"ao")

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("q0005"),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("q0005"))

names(df) <- itemlabels$itemnr

df.omit.na <- df

2.2.2 Alla svar för alla frågor

Code
RIallresp(df.omit.na)
Response category Number of responses Percent
Instämmer helt och hållet 1429 52.9
Instämmer till stor del 904 33.5
Instämmer till viss del 313 11.6
Instämmer inte alls 54 2.0
Code
# koda om svarskategorier till siffror
df.omit.na <- df.omit.na %>% 
  mutate(across(everything(), ~ car::recode(.x,"'Instämmer inte alls'=0;
                                            'Instämmer till viss del' =1;
                                            'Instämmer till stor del'=2;
                                            'Instämmer helt och hållet'=3",
                                            as.factor = FALSE)))

2.2.3 Descriptives - all items

Code
RItileplot(df.omit.na)

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

Code
RIbarplot(df.omit.na)

Code
RIrawdist(df.omit.na)

Vi har väldigt få svar i de lägsta svarskategorierna för items 1-3, men det kan bero på underlaget. Tillsvidare slår vi ihop de två lägsta svarskategorierna för dessa items för att kunna utföra analyserna.

Code
#Temporärt slå ihop svarskategorier så att analyserna funkar

df.omit.na$ao1<-recode(df.omit.na$ao1,"0=0;1=0;2=1;3=2",as.factor=FALSE)
df.omit.na$ao2<-recode(df.omit.na$ao2,"0=0;1=0;2=1;3=2",as.factor=FALSE)
df.omit.na$ao3<-recode(df.omit.na$ao3,"0=0;1=0;2=1;3=2",as.factor=FALSE)

2.3 Rasch-analys 1

itemnr item
ao1 Jag vet vilka arbetsuppgifter jag har.
ao2 Jag vet hur mitt arbete ska utföras.
ao3 Jag vet vilka resultat som jag ska uppnå med mitt arbete.
ao4 Det finns tydliga mål för min arbetsgrupp.
ao5 Det finns tydliga mål för min organisation.
Code
RIitemfitPCM(df.omit.na, samplesize = 250, nsamples = 32)
OutfitMSQ InfitMSQ OutfitZSTD InfitZSTD
ao1 0.847 0.815 -1.473 -2.121
ao2 1.101 1.033 0.971 0.379
ao3 0.683 0.678 -2.989 -3.58
ao4 0.712 0.726 -3.072 -2.86
ao5 0.984 0.987 -0.18 -0.166
Code
RIpcmPCA(na.omit(df.omit.na))
PCA of Rasch model residuals
Eigenvalues
2.13
1.10
0.91
0.87
0.00
Code
RIresidcorr(df.omit.na, cutoff = 0.2)
ao1 ao2 ao3 ao4 ao5
ao1
ao2 0.14
ao3 -0.05 0.08
ao4 -0.32 -0.47 -0.33
ao5 -0.46 -0.53 -0.33 0.14
Note:
Relative cut-off value (highlighted in red) is -0.012, which is 0.2 above the average correlation.
Code
RIloadLoc(df.omit.na)

Code
RIitemCats(df.omit.na, items = "all", xlims = c(-5,5))

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

Code
RIitemHierarchy(df.omit.na)

Vi testar att ta bort ao2 eftersom detta item har residualkorrelationer med både ao1 och ao3.

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

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

2.4 Rasch-analys 2

itemnr item
ao1 Jag vet vilka arbetsuppgifter jag har.
ao3 Jag vet vilka resultat som jag ska uppnå med mitt arbete.
ao4 Det finns tydliga mål för min arbetsgrupp.
ao5 Det finns tydliga mål för min organisation.
Code
RIitemfitPCM(df2, samplesize = 250, nsamples = 32)
OutfitMSQ InfitMSQ OutfitZSTD InfitZSTD
ao1 1.034 0.948 0.34 -0.334
ao3 0.844 0.779 -1.534 -2.503
ao4 0.621 0.622 -4.508 -4.427
ao5 0.844 0.844 -1.638 -1.617

2.4.1 PCA

Code
RIpcmPCA(na.omit(df2))
PCA of Rasch model residuals
Eigenvalues
1.84
1.16
0.99
0.01

2.4.2 Residual correlations

Code
RIresidcorr(df2, cutoff = 0.2)
ao1 ao3 ao4 ao5
ao1
ao3 0.04
ao4 -0.35 -0.38
ao5 -0.52 -0.41 -0.05
Note:
Relative cut-off value (highlighted in red) is -0.081, which is 0.2 above the average correlation.

2.4.3 1st contrast loadings

Code
RIloadLoc(df2)

2.4.4 Targeting

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

2.4.5 Item hierarchy

Code
RIitemHierarchy(df2)

2.4.6 Person fit

Code
RIpfit(df2)

Det finns inte längre något problem med residualkorrelationer. Dock finns det fortfarande en något låg item fit för ao4 och en bristfällig matchning mellan items och respondenter. Vi går vidare och undersöker DIF och reliabilitet.

2.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."

Ingen DIF baserat på kön, ålder, bransch eller hemarbete.

2.6 Reliability

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

Reliabiliteten är låg och kommer inte upp i önskvärda nivåer någonstans på skalan. Vi går därför inte vidare och tar fram mätvärden. Frågorna i detta område bör enbart redovisas med svarsfördelning för frågorna enskilt och ej summeras på något vis.

2.7 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 = 45, options = "hover") %>% 
  row_spec(2, background = "lightpink") %>% 
  footnote(general_title = "Notera: ",
           "Borttaget item markerat med färg.")
itemnr item
ao1 Jag vet vilka arbetsuppgifter jag har.
ao2 Jag vet hur mitt arbete ska utföras.
ao3 Jag vet vilka resultat som jag ska uppnå med mitt arbete.
ao4 Det finns tydliga mål för min arbetsgrupp.
ao5 Det finns tydliga mål för min organisation.
Notera:
Borttaget item markerat med färg.