Extending Types

While PsychometricTests.jl implements minimal versions for test components with BasicItem, BasicPerson, and BasicResponse, more compex use cases might require extensions of these basic components. In this example we will create a psychometric test that not only includes variables on the person level, but also response times for the item responses. The resulting PsychometricTest stores the required information and has the capability to analyse response times.

Adding Person variables

In a first step we add additional variables to the persons field of the psychometric test. For this we need to create a new struct that inherits from Person. This struct contains the persons anger score in an anger field. Additionally the interface definition of Person requires us to add a unique identifier for each person. We include this in an id field of the struct.

using PsychometricTests

struct AngryPerson{T} <: Person
    id::T
    anger::Int
end

The Person interface requires a getid method for all implementations. The default method accesses the id field of the struct. Since the unique person identifier is stored in the id field of AngryPerson, no further implementations are required.

Adding response times

The second step in this example is to add response times to BasicResponse. Similarly to adding the person variables we create a struct inheriting from Response and implement the required interface. The new TimedResponse has additional start_time and end_time variables that can be used to calculate the response times.

using Dates

struct TimedResponse{IIT,PIT,T} <: Response
    item_id::IIT
    person_id::PIT
    value::T
    start_time::DateTime
    end_time::DateTime
end

The interface for Response requires implemtation of getvalue, getitemid, and getpersonid. The default methods access the value, item_id and person_id of the struct respectively. Just as before, no further interface implementations are necessary in this case.

Additionally we want to be able to get the response time for a given response, so we define a custom response_time function.

response_time(response::TimedResponse) = response.end_time - response.start_time
response_time (generic function with 1 method)

This concludes the type definition section of this example. Next, we will move on to constructing the psychometric test.

Constructing the test

To construct a psychometric test from our custom structs we need some items, persons, and responses. In this example we will simply use randomly generated data.

For items we just need some BasicItem,

items = [BasicItem(i) for i in 1:2]
2-element Vector{BasicItem{Int64}}:
 BasicItem{Int64}(1)
 BasicItem{Int64}(2)

Persons have some anger score (0 - 20) in addition to a unique id,

persons = [AngryPerson(p, rand(0:20)) for p in 1:3]
3-element Vector{Main.var"Main".AngryPerson{Int64}}:
 Main.var"Main".AngryPerson{Int64}(1, 17)
 Main.var"Main".AngryPerson{Int64}(2, 17)
 Main.var"Main".AngryPerson{Int64}(3, 4)

Similarly, the TimedResponse for the test will have some randomly generated timings,

responses = TimedResponse{Int,Int,Int}[]

for i in 1:2, p in 1:3
    start_time = now()
    end_time = start_time + Second(rand(10:600))
    response = TimedResponse(i, p, rand(0:1), start_time, end_time)
    push!(responses, response)
end

responses
6-element Vector{Main.var"Main".TimedResponse{Int64, Int64, Int64}}:
 Main.var"Main".TimedResponse{Int64, Int64, Int64}(1, 1, 0, Dates.DateTime("2023-10-11T13:53:28.767"), Dates.DateTime("2023-10-11T14:03:23.767"))
 Main.var"Main".TimedResponse{Int64, Int64, Int64}(1, 2, 0, Dates.DateTime("2023-10-11T13:53:28.775"), Dates.DateTime("2023-10-11T14:00:42.775"))
 Main.var"Main".TimedResponse{Int64, Int64, Int64}(1, 3, 1, Dates.DateTime("2023-10-11T13:53:28.775"), Dates.DateTime("2023-10-11T13:58:40.775"))
 Main.var"Main".TimedResponse{Int64, Int64, Int64}(2, 1, 1, Dates.DateTime("2023-10-11T13:53:28.775"), Dates.DateTime("2023-10-11T13:55:33.775"))
 Main.var"Main".TimedResponse{Int64, Int64, Int64}(2, 2, 0, Dates.DateTime("2023-10-11T13:53:28.775"), Dates.DateTime("2023-10-11T13:58:03.775"))
 Main.var"Main".TimedResponse{Int64, Int64, Int64}(2, 3, 1, Dates.DateTime("2023-10-11T13:53:28.775"), Dates.DateTime("2023-10-11T14:03:01.775"))

The last step is to finally construct the psychometric test.

timed_test = PsychometricTest(items, persons, responses)

The timed_test is subject to our usual analyses, such as calculating scores.

personscores(timed_test)

Additionally we can efficiently query the custom fields, e.g.

response_times = [response_time(r) for r in eachresponse(timed_test)]