Title: | Visualise the Path to a Stopping Point in Qualitative Interviews Based on Novel Codes |
---|---|
Description: | In semi-structured interviews that use the 'framework' method, it is not always clear how refinements to interview questions affect the decision of when to stop interviews. The trend of 'novel' and 'duplicate' interview codes (novel codes are information that other interviewees have not previously mentioned) provides insight into the richness of qualitative information. This package provides tools to visualise when refinements occur and how that affects the trends of novel and duplicate codes. These visualisations, when used progressively as new interviews are finished, can help the researcher to decide on a stopping point for their interviews. |
Authors: | Kam Wong [aut, cph] |
Maintainer: | Desi Quintans <[email protected]> |
License: | MIT + file LICENSE |
Version: | 0.13.1 |
Built: | 2025-03-04 03:02:18 UTC |
Source: | https://github.com/desiquintans/novelqualcodes |
It is good practice to record when refinements were made, what was done, and
the reasons behind them. These "field notes" are used by this package to
annotate plots created by plot_novelty()
and plot_richness()
, and they're
also requested by peer reviewers as part of the publication process.
This template contains 5 columns (only the first one, interview_num
,
is currently used by the package, but this is subject to change):
interview_num
is the upcoming interview number where these refinements
will take effect.
refinement_type
is free text describing the kind of refinement,
e.g. "add" or "rephrase" or "remove".
refinement
is the actual text that has been changed.
reason
describes the rationale behind the refinement.
other
is for additional information you may want to include.
create_field_notes_template(path = stop("A save path must be specified.")) create_fieldnotes_template(path = stop("A save path must be specified."))
create_field_notes_template(path = stop("A save path must be specified.")) create_fieldnotes_template(path = stop("A save path must be specified."))
path |
(Character) The path where the field notes template should be created. |
Invisibly returns the path to the template (Character). In an
interactive session, also opens path
in the system's file viewer.
# Create the template in a temporary directory. create_field_notes_template(path = tempdir())
# Create the template in a temporary directory. create_field_notes_template(path = tempdir())
Coding matrices are built inside NVivo's Matrix Coding Query tool, with codes as rows and one participant ("case") as column. These files should be exported as Excel spreadsheets (XLS or XLSX format), which is the default for NVivo. There must only be one participant per file.
Filenames must reflect the chronological order of interviews when they are sorted. You can do this by naming them in sequence like "Interview 07 PID 2345", or by including a YMD HM timestamp like "2023-06-17 1345". Sorting is number-aware and only uses the filename itself (i.e. file path is ignored during sorting).
import_coding_matrices(path, recursive = FALSE)
import_coding_matrices(path, recursive = FALSE)
path |
(Character) Path to a folder that contains coding matrices exported from NVivo (Explore then Matrix Coding Query then Export Coding Matrix). All files with .XLS or .XLSX extensions will be imported. |
recursive |
(Logical) If |
A list of dataframes.
score_codes()
, import_field_notes()
# A folder of example coding matrices included with the package path_to_matrices <- system.file("insect_study/matrices/", package = "novelqualcodes") print(path_to_matrices) # A list of files in that folder list.files(path_to_matrices) # Import them all at once my_matrices <- import_coding_matrices(path_to_matrices) # Look inside the result; each entry of 'my_matrices' is an interview, listed # in chronological order. print(my_matrices)
# A folder of example coding matrices included with the package path_to_matrices <- system.file("insect_study/matrices/", package = "novelqualcodes") print(path_to_matrices) # A list of files in that folder list.files(path_to_matrices) # Import them all at once my_matrices <- import_coding_matrices(path_to_matrices) # Look inside the result; each entry of 'my_matrices' is an interview, listed # in chronological order. print(my_matrices)
'Field notes' in this context is a spreadsheet that records the refinements
that a researcher makes throughout their interview process. This package is
opinionated about what these field notes should look like: use create_field_notes_template()
to get a template for what the package accepts.
import_field_notes(path, ...) import_fieldnotes(path, ...)
import_field_notes(path, ...) import_fieldnotes(path, ...)
path |
(Character) The full path (including filename) to the Excel spreadsheet. |
... |
Other named arguments that will be passed to |
A named list of class field_notes
.
# An example field notes spreadsheet included with the package. path_to_notes <- system.file("insect_study/records/refinements.xlsx", package = "novelqualcodes") print(path_to_notes) # Importing the spreadsheet my_refinements <- import_field_notes(path_to_notes) # Looking at its contents str(my_refinements, max.level = 1) print(my_refinements$df)
# An example field notes spreadsheet included with the package. path_to_notes <- system.file("insect_study/records/refinements.xlsx", package = "novelqualcodes") print(path_to_notes) # Importing the spreadsheet my_refinements <- import_field_notes(path_to_notes) # Looking at its contents str(my_refinements, max.level = 1) print(my_refinements$df)
Novel codes are information that has not been previously mentioned by other interviewees. The trend of 'novel' interview codes provides insight into the richness of qualitative information.
This plot shows the trend of novel code generation; in the most basic way, the higher the number, the richer the information that has been generated in the study. By showing novel codes in context with any refinements to the questions, it also shows how that trend may have been affected by those refinements, and whether novel code generation is plateauing.
This chart alone should not be used to decide on a stopping point because it
does not show the richness of individual interviews; some interviews are richer
than others, therefore consider also using plot_richness()
to look at the
richness of each interview in terms of novel and duplicate codes.
plot_novelty( score_df, refinements = integer(0), col = list(stroke = "black", fill_ref = "black", fill = "grey80") )
plot_novelty( score_df, refinements = integer(0), col = list(stroke = "black", fill_ref = "black", fill = "grey80") )
score_df |
(Dataframe) A dataframe of scored codes, as generated by |
refinements |
Either a list object generated by |
col |
(List) A List containing named Character vectors. Accepted names are:
|
A ggplot object.
score_codes()
, import_field_notes()
, plot_richness()
, save_last_plot()
# Field notes and coding matrices included with the package path_to_notes <- system.file("insect_study/records/refinements.xlsx", package = "novelqualcodes") path_to_matrices <- system.file("insect_study/matrices/", package = "novelqualcodes") # Import the data my_refinements <- import_field_notes(path_to_notes) my_matrices <- import_coding_matrices(path_to_matrices) # Score novel and duplicate codes my_scores <- score_codes(my_matrices) # Generate a plot with no refinements plot_novelty(score_df = my_scores) # Generate a plot using scored codes and imported refinements plot_novelty(score_df = my_scores, refinements = my_refinements) # Generate a plot using scored codes and a vector of refinement times plot_novelty(score_df = my_scores, refinements = c(4, 8, 10))
# Field notes and coding matrices included with the package path_to_notes <- system.file("insect_study/records/refinements.xlsx", package = "novelqualcodes") path_to_matrices <- system.file("insect_study/matrices/", package = "novelqualcodes") # Import the data my_refinements <- import_field_notes(path_to_notes) my_matrices <- import_coding_matrices(path_to_matrices) # Score novel and duplicate codes my_scores <- score_codes(my_matrices) # Generate a plot with no refinements plot_novelty(score_df = my_scores) # Generate a plot using scored codes and imported refinements plot_novelty(score_df = my_scores, refinements = my_refinements) # Generate a plot using scored codes and a vector of refinement times plot_novelty(score_df = my_scores, refinements = c(4, 8, 10))
The full definition of novel and duplicate codes is in score_codes()
.
Briefly, 'novel' codes are topics/ideas/concepts that were not mentioned in
previous interviews, whereas 'duplicate' codes are topics that other
interviews have discussed previously.
plot_richness( score_df, refinements = integer(0), col = list(stroke_novel = "black", stroke_duplicate = "gray80", fill_novel = "black", fill_duplicate = "gray90") )
plot_richness( score_df, refinements = integer(0), col = list(stroke_novel = "black", stroke_duplicate = "gray80", fill_novel = "black", fill_duplicate = "gray90") )
score_df |
(Dataframe) A dataframe of scored codes, as generated by |
refinements |
Either a list object generated by |
col |
(List) A List containing named Character vectors. Accepted names are:
|
Some interviews will touch on many different topics and generate many different
codes, whereas other interviews will be brief or limited. We call this 'richness'.
This plot complements plot_novelty()
by visualising the richness of each
interview in terms of novel and duplicate codes, in context with any
refinements to interview questions that were made (marked by stars underneath
each bar). By examining this plot together with their field notes, researchers
can get insight into the effects of their refinements and the richness of the
data.
A ggplot object.
score_codes()
, import_field_notes()
, plot_novelty()
, save_last_plot()
# Field notes and coding matrices included with the package path_to_notes <- system.file("insect_study/records/refinements.xlsx", package = "novelqualcodes") path_to_matrices <- system.file("insect_study/matrices/", package = "novelqualcodes") # Import the data my_refinements <- import_field_notes(path_to_notes) my_matrices <- import_coding_matrices(path_to_matrices) # Score novel and duplicate codes my_scores <- score_codes(my_matrices) # Generate a plot with no refinements plot_richness(score_df = my_scores) # Generate a plot using scored codes and imported refinements plot_richness(score_df = my_scores, refinements = my_refinements) # Generate a plot using scored codes and a vector of refinement times plot_richness(score_df = my_scores, refinements = c(4, 8, 10))
# Field notes and coding matrices included with the package path_to_notes <- system.file("insect_study/records/refinements.xlsx", package = "novelqualcodes") path_to_matrices <- system.file("insect_study/matrices/", package = "novelqualcodes") # Import the data my_refinements <- import_field_notes(path_to_notes) my_matrices <- import_coding_matrices(path_to_matrices) # Score novel and duplicate codes my_scores <- score_codes(my_matrices) # Generate a plot with no refinements plot_richness(score_df = my_scores) # Generate a plot using scored codes and imported refinements plot_richness(score_df = my_scores, refinements = my_refinements) # Generate a plot using scored codes and a vector of refinement times plot_richness(score_df = my_scores, refinements = c(4, 8, 10))
Manually reshapes the data into Long format and adds a refinement group factor.
reshape_for_plots(score_df, refinements = integer(0))
reshape_for_plots(score_df, refinements = integer(0))
score_df |
(Dataframe) A scored code matrix as generated by |
refinements |
Either a list object generated by |
A dataframe.
Save the most recent plot to a file
save_last_plot( filename = stop("A save path and filename must be specified."), size = "4 x 3 in", dpi = 300, ... )
save_last_plot( filename = stop("A save path and filename must be specified."), size = "4 x 3 in", dpi = 300, ... )
filename |
(Character) The path and filename of the file to create. |
size |
(Character) The output size of the file, in the form |
dpi |
(Integer) The resolution (dots per inch) of the output file. |
... |
Other arguments passed to |
This function returns nothing, but has the side-effect of writing a file to filename
.
plot_novelty()
, plot_richness()
# Coding matrices included with the package path_to_matrices <- system.file("insect_study/matrices/", package = "novelqualcodes") # Import the data my_matrices <- import_coding_matrices(path_to_matrices) # Score novel and duplicate codes my_scores <- score_codes(my_matrices) # Generate a plot with no refinements plot_richness(score_df = my_scores) # Save it to a temporary directory save_last_plot(file.path(tempdir(), "test_plot.png"), size = "4 x 3 in") # Open the temporary directory (if session is running interactively) if (interactive()) { utils::browseURL(tempdir()) }
# Coding matrices included with the package path_to_matrices <- system.file("insect_study/matrices/", package = "novelqualcodes") # Import the data my_matrices <- import_coding_matrices(path_to_matrices) # Score novel and duplicate codes my_scores <- score_codes(my_matrices) # Generate a plot with no refinements plot_richness(score_df = my_scores) # Save it to a temporary directory save_last_plot(file.path(tempdir(), "test_plot.png"), size = "4 x 3 in") # Open the temporary directory (if session is running interactively) if (interactive()) { utils::browseURL(tempdir()) }
'Novel' and 'duplicate' codes are scored once per interview; the number of times they are spoken in an interview does not matter.
The definition of whether a code is novel or duplicated is entirely chronological:
A novel code is a topic/idea/concept that, for example, is mentioned in Interview 17, but was not mentioned in Interviews 1 through 16.
A duplicate code is one that has been talked about in other interviews previously.
The cumulative sum of novel codes is used to visualise a stopping point for qualitative interviews.
score_codes(interviews)
score_codes(interviews)
interviews |
(List) A list of dataframes, as generated by |
A dataframe, with one row per interview and these columns:
itvw_seq
, the chronological order of interviews.
n_codes
, the number of unique codes mentioned in this interview.
n_duplicate
, how many of those codes are duplicates mentioned in previous interviews).
n_novel
, how many of those codes are novel (mentioned for the first time in this interview).
prop_duplicate
, the proportion of this interview's codes that are duplicates.
prop_novel
, the proportion of this interview's codes that are novel.
cumsum_novel
, the cumulative sum of novel codes over time (i.e. across interviews).
plot_novelty()
, plot_richness()
# A folder of example coding matrices included with the package path_to_matrices <- system.file("insect_study/matrices/", package = "novelqualcodes") print(path_to_matrices) # A list of files in that folder list.files(path_to_matrices) # Import them all at once my_matrices <- import_coding_matrices(path_to_matrices) # Score them for novel and duplicate codes my_scores <- score_codes(my_matrices) # Look inside the result; novel and duplicate codes are scored across # all interviews. print(my_scores)
# A folder of example coding matrices included with the package path_to_matrices <- system.file("insect_study/matrices/", package = "novelqualcodes") print(path_to_matrices) # A list of files in that folder list.files(path_to_matrices) # Import them all at once my_matrices <- import_coding_matrices(path_to_matrices) # Score them for novel and duplicate codes my_scores <- score_codes(my_matrices) # Look inside the result; novel and duplicate codes are scored across # all interviews. print(my_scores)