2020-04-21 20:45:15 +00:00
|
|
|
// Data file not 100% clear, bus seems to be: DIFData.laz
|
2020-04-21 20:00:11 +00:00
|
|
|
// - Reference Group Code: 1
|
|
|
|
// - Focal Group Code: 2
|
|
|
|
// - No. of Score Levels: 11
|
|
|
|
// - Lower/Upper Bounds: 0-3, 4-7, 8-11, 12-15, 16-19, 20-23, 24-27, 28-31, 32-35, 36-39, 40-43
|
2020-04-21 20:45:15 +00:00
|
|
|
// The result obtained this way match the pdf file.
|
2020-04-21 20:00:11 +00:00
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
unit DifUnit;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
|
|
|
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
|
|
|
|
StdCtrls, Buttons, ExtCtrls,
|
|
|
|
MainUnit, Globals, OutputUnit, MatrixLib, FunctionsLib, GraphLib, ContextHelpUnit;
|
|
|
|
|
|
|
|
type
|
|
|
|
DynamicCharArray = array of char;
|
|
|
|
|
|
|
|
{ TDIFFrm }
|
|
|
|
|
|
|
|
TDIFFrm = class(TForm)
|
|
|
|
Bevel1: TBevel;
|
2020-04-21 20:00:11 +00:00
|
|
|
NextBtn: TButton;
|
|
|
|
LevelNoEdit: TStaticText;
|
|
|
|
LevelsGroup: TGroupBox;
|
2020-03-30 18:01:44 +00:00
|
|
|
HelpBtn: TButton;
|
|
|
|
Panel1: TPanel;
|
|
|
|
Panel2: TPanel;
|
|
|
|
Panel3: TPanel;
|
|
|
|
ResetBtn: TButton;
|
|
|
|
ComputeBtn: TButton;
|
2020-04-21 20:00:11 +00:00
|
|
|
CloseBtn: TButton;
|
2020-03-30 18:01:44 +00:00
|
|
|
ItemStatsChk: TCheckBox;
|
|
|
|
TestStatsChk: TCheckBox;
|
|
|
|
ItemCorrsChk: TCheckBox;
|
|
|
|
ItemTestChk: TCheckBox;
|
|
|
|
AlphaChk: TCheckBox;
|
|
|
|
MHChk: TCheckBox;
|
|
|
|
LogisticChk: TCheckBox;
|
|
|
|
CurvesChk: TCheckBox;
|
|
|
|
CountsChk: TCheckBox;
|
|
|
|
RefGrpEdit: TEdit;
|
|
|
|
TrgtGrpEdit: TEdit;
|
|
|
|
LevelsEdit: TEdit;
|
|
|
|
LowBoundEdit: TEdit;
|
|
|
|
UpBoundEdit: TEdit;
|
2020-04-21 20:00:11 +00:00
|
|
|
OptionsGroup: TGroupBox;
|
2020-03-30 18:01:44 +00:00
|
|
|
ItemInBtn: TBitBtn;
|
|
|
|
ItemOutBtn: TBitBtn;
|
|
|
|
AllBtn: TBitBtn;
|
|
|
|
GrpInBtn: TBitBtn;
|
|
|
|
GrpOutBtn: TBitBtn;
|
|
|
|
GroupVarEdit: TEdit;
|
|
|
|
Label1: TLabel;
|
|
|
|
Label10: TLabel;
|
|
|
|
Label11: TLabel;
|
|
|
|
UpBoundlabel: TLabel;
|
|
|
|
Label2: TLabel;
|
|
|
|
ItemsList: TListBox;
|
|
|
|
Label3: TLabel;
|
|
|
|
Label4: TLabel;
|
|
|
|
Label5: TLabel;
|
|
|
|
Label6: TLabel;
|
|
|
|
Label8: TLabel;
|
|
|
|
Label9: TLabel;
|
|
|
|
LevelScroll: TScrollBar;
|
|
|
|
VarList: TListBox;
|
|
|
|
procedure AllBtnClick(Sender: TObject);
|
|
|
|
procedure ComputeBtnClick(Sender: TObject);
|
|
|
|
procedure FormActivate(Sender: TObject);
|
|
|
|
procedure FormCreate(Sender: TObject);
|
|
|
|
procedure FormShow(Sender: TObject);
|
|
|
|
procedure HelpBtnClick(Sender: TObject);
|
2020-04-21 20:00:11 +00:00
|
|
|
procedure LevelScrollChange(Sender: TObject);
|
|
|
|
procedure LevelsEditEditingDone(Sender: TObject);
|
|
|
|
procedure LowBoundEditEditingDone(Sender: TObject);
|
|
|
|
procedure NextBtnClick(Sender: TObject);
|
|
|
|
procedure UpBoundEditEditingDone(Sender: TObject);
|
2020-03-30 18:01:44 +00:00
|
|
|
procedure GrpInBtnClick(Sender: TObject);
|
|
|
|
procedure GrpOutBtnClick(Sender: TObject);
|
|
|
|
procedure ItemInBtnClick(Sender: TObject);
|
|
|
|
procedure ItemOutBtnClick(Sender: TObject);
|
|
|
|
procedure ResetBtnClick(Sender: TObject);
|
2020-04-21 20:00:11 +00:00
|
|
|
procedure VarListSelectionChange(Sender: TObject; User: boolean);
|
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
private
|
|
|
|
{ private declarations }
|
2020-04-21 20:00:11 +00:00
|
|
|
NoItems: integer;
|
|
|
|
NoLevels: integer;
|
|
|
|
tmean, tvar, tsd: double;
|
|
|
|
ColNoSelected: IntDyneVec;
|
|
|
|
ColLabels, RowLabels: StrDyneVec;
|
|
|
|
Means, Variances, StdDevs: DblDyneVec;
|
|
|
|
CorMat: DblDyneMat; // correlations among items and total score
|
|
|
|
Data: IntDyneMat; //store item scores and total score
|
|
|
|
Ubounds: IntDyneVec; // upper and lower bounds of score groups
|
|
|
|
Lbounds: IntdyneVec;
|
|
|
|
Code: DynamicCharArray; // blank, A, B or C ETS codes
|
|
|
|
Level10OK: IntdyneMat; // check that each item category >= 10
|
|
|
|
RMHRight: IntDyneMat; // no. right for items by score group in reference group
|
|
|
|
RMHWrong: IntDyneMat; // no. wrong for items by score group in reference group
|
|
|
|
FMHRight: IntDyneMat; // no. right for items by score group in focus group
|
|
|
|
FMHWrong: IntDyneMat; // no. wrong for items by score group in focus group
|
|
|
|
RScrGrpCnt: IntDyneMat; // total responses for score groups in reference group
|
|
|
|
FScrGrpCnt: IntDyneMat; // total responses for score groups in focus group
|
|
|
|
NT: IntDyneMat; // total right and wrong in each category of each item
|
|
|
|
Alpha: DblDyneVec;
|
|
|
|
AlphaNum: DblDyneVec;
|
|
|
|
AlphaDen: DblDyneVec;
|
|
|
|
MHDiff: DblDyneVec;
|
|
|
|
ExpA: DblDyneMat;
|
|
|
|
VarA: DblDyneMat;
|
|
|
|
SumA: DblDyneVec;
|
|
|
|
SumExpA: DblDyneVec;
|
|
|
|
SumVarA: DblDyneVec;
|
|
|
|
ChiSqr: DblDyneVec;
|
|
|
|
Prob: DblDyneVec;
|
|
|
|
SEMHDDif: DblDyneVec;
|
|
|
|
Aster: StrDyneVec;
|
|
|
|
C: DblDyneVec;
|
|
|
|
CodeRF: DynamicCharArray;
|
|
|
|
Tot: IntDyneVec;
|
|
|
|
|
|
|
|
FAutoSized: Boolean;
|
|
|
|
procedure UpdateBtnStates;
|
|
|
|
function ValidNumEdit(AEdit: TEdit; AName: String; out AValue: Integer): Boolean;
|
|
|
|
|
|
|
|
procedure AlphaRel(AReport: TStrings);
|
|
|
|
procedure ItemCorrs(AReport: TStrings);
|
|
|
|
procedure ItemTestCorrs(AReport: TStrings);
|
|
|
|
procedure ItemCurves;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
public
|
|
|
|
{ public declarations }
|
|
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
|
|
DIFFrm: TDIFFrm;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
uses
|
2020-04-21 20:00:11 +00:00
|
|
|
Math, Utils;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
{ TDIFFrm }
|
|
|
|
|
|
|
|
procedure TDIFFrm.ResetBtnClick(Sender: TObject);
|
2020-04-21 20:00:11 +00:00
|
|
|
var
|
|
|
|
i: integer;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
//allocate space on heap
|
|
|
|
SetLength(ColLabels, NoVariables + 1);
|
|
|
|
SetLength(RowLabels, NoVariables + 1);
|
|
|
|
SetLength(Means, NoVariables);
|
|
|
|
SetLength(Variances, NoVariables);
|
|
|
|
SetLength(StdDevs, NoVariables);
|
|
|
|
SetLength(CorMat, NoVariables, NoVariables);
|
|
|
|
SetLength(Data, NoCases, NoVariables + 3); //group, items, total, flag
|
|
|
|
SetLength(Lbounds, NoVariables);
|
|
|
|
SetLength(Ubounds, NoVariables);
|
|
|
|
SetLength(Tot, NoCases);
|
|
|
|
SetLength(ColNoSelected, NoVariables);
|
|
|
|
for i:=0 to High(LBounds) do LBounds[i] := -1;
|
|
|
|
for i:=0 to High(UBounds) do UBounds[i] := -1;
|
|
|
|
|
|
|
|
VarList.Clear;
|
|
|
|
ItemsList.Clear;
|
|
|
|
GroupVarEdit.Text := '';
|
|
|
|
for i := 1 to NoVariables do
|
|
|
|
VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]);
|
|
|
|
|
|
|
|
ItemStatsChk.Checked := true;
|
|
|
|
TestStatsChk.Checked := false;
|
|
|
|
ItemCorrsChk.Checked := false;
|
|
|
|
ItemTestChk.Checked := false;
|
|
|
|
MHChk.Checked := true;
|
|
|
|
LogisticChk.Checked := false;
|
|
|
|
|
|
|
|
RefGrpEdit.Text := '';
|
|
|
|
TrgtGrpEdit.Text := '';
|
|
|
|
|
|
|
|
LevelScroll.Min := 0;
|
|
|
|
LevelScroll.Max := 0;
|
|
|
|
LevelScroll.Position := 0;
|
|
|
|
|
|
|
|
LevelsEdit.Text := '';
|
|
|
|
LevelNoEdit.Caption := '';
|
|
|
|
LowBoundEdit.Text := '';
|
|
|
|
UpBoundEdit.Text := '';
|
|
|
|
|
|
|
|
UpdateBtnStates;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDIFFrm.FormActivate(Sender: TObject);
|
|
|
|
var
|
|
|
|
w: Integer;
|
|
|
|
begin
|
|
|
|
if FAutoSized then
|
|
|
|
exit;
|
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
w := MaxValue([HelpBtn.Width, ResetBtn.Width, ComputeBtn.Width, CloseBtn.Width]);
|
2020-03-30 18:01:44 +00:00
|
|
|
HelpBtn.Constraints.MinWidth := w;
|
|
|
|
ResetBtn.Constraints.MinWidth := w;
|
|
|
|
ComputeBtn.Constraints.MinWidth := w;
|
2020-04-21 20:00:11 +00:00
|
|
|
CloseBtn.Constraints.MinWidth := w;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
VarList.Constraints.MinWidth := GroupVarEdit.Width;
|
2020-04-21 20:00:11 +00:00
|
|
|
Panel1.Constraints.MinHeight := OptionsGroup.Height - Label1.Height;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
|
|
|
Constraints.MinWidth := Width;
|
|
|
|
Constraints.MinHeight := Height;
|
|
|
|
|
|
|
|
FAutoSized := true;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDIFFrm.FormCreate(Sender: TObject);
|
|
|
|
begin
|
|
|
|
Assert(OS3MainFrm <> nil);
|
2020-04-21 20:00:11 +00:00
|
|
|
if GraphFrm = nil then GraphFrm := TGraphFrm.Create(Application);
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDIFFrm.FormShow(Sender: TObject);
|
|
|
|
begin
|
|
|
|
ResetBtnClick(Self);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDIFFrm.GrpInBtnClick(Sender: TObject);
|
2020-04-21 20:00:11 +00:00
|
|
|
var
|
|
|
|
index: integer;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
index := VarList.ItemIndex;
|
|
|
|
if (index > -1) and (GroupVarEdit.Text = '') then
|
|
|
|
begin
|
|
|
|
GroupVarEdit.Text := VarList.Items[index];
|
|
|
|
VarList.Items.Delete(index);
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDIFFrm.GrpOutBtnClick(Sender: TObject);
|
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
if GroupVarEdit.Text <> '' then
|
|
|
|
begin
|
|
|
|
VarList.Items.Add(GroupVarEdit.Text);
|
|
|
|
GroupVarEdit.Text := '';
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDIFFrm.ItemInBtnClick(Sender: TObject);
|
2020-04-21 20:00:11 +00:00
|
|
|
var
|
|
|
|
i: integer;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
i := 0;
|
|
|
|
while i < VarList.Items.Count do
|
|
|
|
begin
|
|
|
|
if VarList.Selected[i] then
|
|
|
|
begin
|
|
|
|
ItemsList.Items.Add(VarList.Items[i]);
|
|
|
|
VarList.Items.Delete(i);
|
|
|
|
i := 0;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
i := i + 1;
|
|
|
|
end;
|
|
|
|
UpdateBtnStates;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDIFFrm.ItemOutBtnClick(Sender: TObject);
|
2020-04-21 20:00:11 +00:00
|
|
|
var
|
|
|
|
i: integer;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
i := 0;
|
|
|
|
while i < ItemsList.Items.Count do
|
|
|
|
begin
|
|
|
|
if ItemsList.Selected[i] then
|
|
|
|
begin
|
|
|
|
VarList.Items.Add(ItemsList.Items[i]);
|
|
|
|
ItemsList.Items.Delete(i);
|
|
|
|
i := 0;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
i := i + 1;
|
|
|
|
end;
|
|
|
|
UpdateBtnStates;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
procedure TDIFFrm.LevelScrollChange(Sender: TObject);
|
2020-03-30 18:01:44 +00:00
|
|
|
var
|
2020-04-21 20:00:11 +00:00
|
|
|
level: Integer;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
level := LevelScroll.Position;
|
|
|
|
LevelNoEdit.Caption := IntToStr(level + 1);
|
|
|
|
|
|
|
|
if LBounds[level] = -1 then
|
|
|
|
LowBoundEdit.Text := ''
|
|
|
|
else
|
|
|
|
LowBoundEdit.Text := IntToStr(LBounds[level]);
|
|
|
|
|
|
|
|
if UBounds[level] = -1 then
|
|
|
|
UpBoundEdit.Text := ''
|
|
|
|
else
|
|
|
|
UpBoundEdit.Text := IntToStr(UBounds[level]);
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
procedure TDIFFrm.LevelsEditEditingDone(Sender: TObject);
|
|
|
|
var
|
|
|
|
L: Integer;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
if ValidNumEdit(LevelsEdit, 'No. of Score Levels', L) then
|
|
|
|
begin
|
|
|
|
LevelScroll.Max := Max(L - 1, 0);
|
|
|
|
LevelScroll.Min := 0;
|
|
|
|
LevelNoEdit.Caption := IntToStr(LevelScroll.Position + 1);
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
procedure TDIFFrm.LowBoundEditEditingDone(Sender: TObject);
|
|
|
|
var
|
|
|
|
level: Integer;
|
|
|
|
n: Integer;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
level := LevelScroll.Position;
|
|
|
|
if TryStrToInt(LowBoundEdit.Text, n) then
|
|
|
|
LBounds[level] := n
|
|
|
|
else
|
|
|
|
MessageDlg(Format('No valid number in lower bound at level #%d.', [level+1]), mtError, [mbOK], 0);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDIFFrm.NextBtnClick(Sender: TObject);
|
|
|
|
begin
|
|
|
|
LevelScroll.Position := LevelScroll.Position + 1;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDIFFrm.ComputeBtnClick(Sender: TObject);
|
|
|
|
var
|
2020-04-21 20:00:11 +00:00
|
|
|
i, j, k : integer;
|
|
|
|
itm : integer;
|
|
|
|
grpvar : integer;
|
|
|
|
subjgrp : integer;
|
|
|
|
value : integer;
|
|
|
|
subjscore : integer;
|
|
|
|
sum : integer;
|
|
|
|
cellstring : string;
|
|
|
|
title : string;
|
|
|
|
nsize : array [1..2] of integer;
|
|
|
|
Rtm, Wtm : double;
|
|
|
|
TotPurge : integer;
|
|
|
|
LoopIt : integer;
|
|
|
|
RItem: integer;
|
|
|
|
lReport: TStrings;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
NoItems := ItemsList.Items.Count;
|
|
|
|
if NoItems = 0 then
|
|
|
|
begin
|
|
|
|
MessageDlg('No items selected.', mtError, [mbOK], 0);
|
|
|
|
exit;
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
if GroupVarEdit.Text = '' then
|
|
|
|
begin
|
|
|
|
MessageDlg('No Grouping Variable selected.', mtError, [mbOK], 0);
|
|
|
|
exit;
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
if not ValidNumEdit(RefGrpEdit, 'Reference Group Code', i) then
|
|
|
|
exit;
|
|
|
|
if not ValidNumEdit(TrgtGrpEdit, 'Focus Group Code', i) then
|
|
|
|
exit;
|
|
|
|
if not ValidNumEdit(LevelsEdit, 'Number of Grouping Levels', noLevels) then
|
|
|
|
exit;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
for i:=0 to StrToInt(LevelsEdit.Caption) - 1 do
|
|
|
|
begin
|
|
|
|
if LBounds[i] = -1 then begin
|
|
|
|
MessageDlg(Format('Lower bound not specified for level #%d.', [i+1]), mtError, [mbOK], 0);
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
if UBounds[i] = -1 then begin
|
|
|
|
MessageDlg(Format('Upper bound not specified for level #%d.', [i+1]), mtError, [mbOk], 0);
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
if LBounds[i] > UBounds[i] then begin
|
|
|
|
MessageDlg(Format('Upper bound must be larger than the lower bound (level #%d).', [i+1]), mtError, [mbOK], 0);
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
lReport := TStringList.Create;
|
|
|
|
try
|
|
|
|
lReport.Add('MANTEL-HAENSZELl DIF ANALYSIS adapted by Bill Miller from');
|
|
|
|
lReport.Add('EZDIF written by Niels G. Waller');
|
|
|
|
lReport.Add('');
|
|
|
|
|
|
|
|
for k := 1 to 2 do
|
|
|
|
nsize[k] := 0;
|
|
|
|
|
|
|
|
// get items to analyze and their labels
|
|
|
|
for i := 1 to NoItems do // items to analyze
|
|
|
|
begin
|
|
|
|
for j := 1 to NoVariables do // variables in grid
|
|
|
|
begin
|
|
|
|
cellstring := OS3MainFrm.DataGrid.Cells[j,0];
|
|
|
|
if cellstring = ItemsList.Items[i-1] then
|
|
|
|
begin // matched - save info
|
|
|
|
ColNoSelected[i-1] := j;
|
|
|
|
ColLabels[i-1] := cellstring;
|
|
|
|
RowLabels[i-1] := cellstring;
|
|
|
|
end; // end match
|
|
|
|
end; // next j
|
|
|
|
end; // next i
|
|
|
|
ColLabels[NoItems] := 'TOTAL';
|
|
|
|
RowLabels[NoItems] := 'TOTAL';
|
|
|
|
|
|
|
|
// get the variable number of the grouping code
|
|
|
|
grpvar := 0;
|
|
|
|
for i := 1 to NoVariables do
|
|
|
|
begin
|
|
|
|
cellstring := OS3MainFrm.DataGrid.Cells[i,0];
|
|
|
|
if cellstring = GroupVarEdit.Text then
|
|
|
|
grpvar := i;
|
|
|
|
end;
|
|
|
|
if grpvar = 0 then
|
|
|
|
begin
|
|
|
|
MessageDlg('No group variable found.', mtError, [mbOK], 0);
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// get number of test score levels
|
|
|
|
nolevels := StrToInt(LevelsEdit.Text);
|
|
|
|
|
|
|
|
// read data (score group and items)
|
|
|
|
for i := 1 to NoCases do
|
|
|
|
begin
|
|
|
|
subjscore := 0;
|
|
|
|
value := round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[grpvar,i])));
|
|
|
|
subjgrp := 0;
|
|
|
|
if value = StrToInt(RefGrpEdit.Text) then subjgrp := 1; // reference grp
|
|
|
|
if value = StrToInt(TrgtGrpEdit.Text) then subjgrp := 2; // target group
|
|
|
|
if subjgrp = 0 then
|
|
|
|
begin
|
|
|
|
MessageDlg('Bad group code for a subject.', mtError, [mbOK], 0);
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
Data[i-1,0] := subjgrp;
|
|
|
|
nsize[subjgrp] := nsize[subjgrp] + 1;
|
|
|
|
for j := 1 to NoItems do
|
|
|
|
begin
|
|
|
|
itm := ColNoSelected[j-1];
|
|
|
|
value := Round(StrToFloat(Trim(OS3MainFrm.DataGrid.Cells[itm,i])));
|
|
|
|
if value = 1 then subjscore := subjscore + 1;
|
|
|
|
Data[i-1,j] := value;
|
|
|
|
end;
|
|
|
|
Tot[i-1] := subjscore;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// obtain item means, variances, standard deviations for total subjects
|
|
|
|
for i := 0 to NoItems-1 do
|
|
|
|
begin
|
|
|
|
Means[i] := 0.0;
|
|
|
|
Variances[i] := 0.0;
|
|
|
|
StdDevs[i] := 0.0;
|
|
|
|
for j := 0 to NoCases - 1 do
|
|
|
|
begin
|
|
|
|
Means[i] := Means[i] + Data[j,i+1];
|
|
|
|
Variances[i] := Variances[i] + sqr(Data[j,i+1]);
|
|
|
|
end;
|
|
|
|
Variances[i] := (Variances[i] - (Means[i] * Means[i] / NoCases)) / (NoCases - 1);
|
|
|
|
if Variances[i] <= 0 then
|
|
|
|
begin
|
|
|
|
MessageDlg(Format('Item %d has zero variance. Unselect the item.', [i+1]), mtError, [mbOk], 0);
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
StdDevs[i] := sqrt(Variances[i]);
|
|
|
|
Means[i] := Means[i] / NoCases;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// obtain total score mean, variance and stddev
|
|
|
|
tmean := 0.0;
|
|
|
|
tvar := 0.0;
|
|
|
|
tsd := 0.0;
|
|
|
|
for i := 0 to NoCases - 1 do
|
|
|
|
begin
|
|
|
|
tmean := tmean + Tot[i];
|
|
|
|
tvar := tvar + (Tot[i] * Tot[i]);
|
|
|
|
end;
|
|
|
|
tvar := (tvar - (tmean * tmean / NoCases)) / (NoCases - 1);
|
|
|
|
tsd := sqrt(tvar);
|
|
|
|
tmean := tmean / NoCases;
|
|
|
|
|
|
|
|
// print descriptives if checked
|
|
|
|
if ItemStatsChk.Checked then
|
|
|
|
begin
|
|
|
|
title := 'Total Means';
|
|
|
|
DynVectorPrint(Means, NoItems, title, ColLabels, NoCases, lReport);
|
|
|
|
title := 'Total Variances';
|
|
|
|
DynVectorPrint(Variances, NoItems, title, ColLabels, NoCases, lReport);
|
|
|
|
title := 'Total Standard Deviations';
|
|
|
|
DynVectorPrint(StdDevs, NoItems, title, ColLabels, NoCases, lReport);
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Show total test score statistics if checked
|
|
|
|
if TestStatsChk.Checked then
|
|
|
|
begin
|
|
|
|
lReport.Add('Total Score: Mean %10.3f', [tmean]);
|
|
|
|
lReport.Add(' Variance %10.3f', [tvar]);
|
|
|
|
lReport.Add(' Std.Dev. %10.3f', [tsd]);
|
|
|
|
lReport.Add('');
|
|
|
|
end;
|
|
|
|
|
|
|
|
lReport.Add(DIVIDER);
|
|
|
|
lReport.Add('');
|
|
|
|
|
|
|
|
lReport.Add('Reference group size: %7d', [nsize[1]]);
|
|
|
|
lReport.Add('Focus group size: %7d', [nsize[2]]);
|
|
|
|
lReport.Add('');
|
|
|
|
lReport.Add(DIVIDER);
|
|
|
|
lReport.Add('');
|
|
|
|
|
|
|
|
// get Cronbach alpha for total group if checked
|
|
|
|
if AlphaChk.Checked then
|
|
|
|
AlphaRel(lReport);
|
|
|
|
|
|
|
|
// Get item intercorrelations for total group if checked
|
|
|
|
if ItemCorrsChk.Checked then
|
|
|
|
begin
|
|
|
|
ItemCorrs(lReport);
|
|
|
|
lReport.Add(DIVIDER);
|
|
|
|
lReport.Add('');
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Get item-total score correlations for total group if checked
|
|
|
|
if ItemTestChk.Checked then
|
|
|
|
begin
|
|
|
|
ItemTestCorrs(lReport);
|
|
|
|
lReport.Add(DIVIDER);
|
|
|
|
lReport.Add('');
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Show upper and lower bounds for score group bins
|
|
|
|
lReport.Add ('Conditioning Levels');
|
|
|
|
lReport.Add ('');
|
|
|
|
lReport.Add ('Lower Upper');
|
|
|
|
lReport.Add ('----- -----');
|
|
|
|
for i := 0 to nolevels-1 do
|
|
|
|
lReport.Add('%5d %5d', [Lbounds[i], Ubounds[i]]);
|
|
|
|
lReport.Add('');
|
|
|
|
lReport.Add(DIVIDER);
|
|
|
|
lReport.Add('');
|
|
|
|
|
|
|
|
// check for zero variance in each group
|
|
|
|
for k := 1 to 2 do // group
|
|
|
|
begin
|
|
|
|
for i := 0 to NoItems-1 do // item
|
|
|
|
begin
|
|
|
|
sum := 0;
|
|
|
|
for j := 0 to NoCases-1 do // subject
|
|
|
|
begin
|
|
|
|
if Data[j,0] = k then // group match ?
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
sum := sum + Data[j,i+1];
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
2020-04-21 20:00:11 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
if (sum = 0) or (sum = NoVariables) then
|
|
|
|
begin
|
|
|
|
MessageDlg(Format('Item %d in group %d has zero variance.', [i+1, k]), mtError, [mbOK], 0);
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Get count of no. right and wrong for each item in each group
|
|
|
|
SetLength(RMHRight,nolevels,NoItems);
|
|
|
|
SetLength(RMHWrong,nolevels,NoItems);
|
|
|
|
SetLength(FMHRight,nolevels,NoItems);
|
|
|
|
SetLength(FMHWrong,nolevels,NoItems);
|
|
|
|
SetLength(RScrGrpCnt,nolevels,NoItems);
|
|
|
|
SetLength(FScrGrpCnt,nolevels,NoItems);
|
|
|
|
SetLength(Code,NoItems);
|
|
|
|
SetLength(Level10OK,nolevels,NoItems);
|
|
|
|
SetLength(NT,nolevels,NoItems);
|
|
|
|
SetLength(Alpha,NoItems);
|
|
|
|
SetLength(AlphaNum,NoItems);
|
|
|
|
SetLength(AlphaDen,NoItems);
|
|
|
|
SetLength(MHDiff,NoItems);
|
|
|
|
SetLength(CodeRF,NoItems);
|
|
|
|
SetLength(ExpA,nolevels,NoItems);
|
|
|
|
SetLength(VarA,nolevels,NoItems);
|
|
|
|
SetLength(SumA,NoItems);
|
|
|
|
SetLength(SumExpA,NoItems);
|
|
|
|
SetLength(SumVarA,NoItems);
|
|
|
|
SetLength(ChiSqr,NoItems);
|
|
|
|
SetLength(Prob,NoItems);
|
|
|
|
SetLength(Aster,NoItems);
|
|
|
|
SetLength(SEMHDDif,NoItems);
|
|
|
|
SetLength(C,NoItems);
|
|
|
|
|
|
|
|
LoopIt := 0;
|
|
|
|
|
|
|
|
repeat
|
|
|
|
LoopIt := LoopIt + 1;
|
|
|
|
|
|
|
|
// clear arrays
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
begin
|
|
|
|
for k := 0 to nolevels-1 do
|
|
|
|
begin
|
|
|
|
RMHRight[k,j] := 0;
|
|
|
|
RMHWrong[k,j] := 0;
|
|
|
|
RScrGrpCnt[k,j] := 0;
|
|
|
|
FMHRight[k,j] := 0;
|
|
|
|
FMHWrong[k,j] := 0;
|
|
|
|
FScrGrpCnt[k,j] := 0;
|
|
|
|
Level10OK[k,j] := 1;
|
|
|
|
NT[k,j] := 0;
|
|
|
|
ExpA[k,j] := 0.0;
|
|
|
|
VarA[k,j] := 0.0;
|
|
|
|
end;
|
|
|
|
Alpha[j] := 0.0;
|
|
|
|
AlphaNum[j] := 0.0;
|
|
|
|
AlphaDen[j] := 0.0;
|
|
|
|
MHDiff[j] := 0.0;
|
|
|
|
CodeRF[j] := ' ';
|
|
|
|
Prob[j] := 0.0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
lReport.Add('COMPUTING M-H CHI-SQUARE, PASS #%d', [LoopIt]);
|
|
|
|
|
|
|
|
for k := 0 to nolevels-1 do
|
|
|
|
begin
|
|
|
|
for i := 0 to NoCases-1 do
|
|
|
|
begin
|
|
|
|
subjgrp := Data[i,0];
|
|
|
|
for j := 0 to NoItems-1 do
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
RItem := 0;
|
|
|
|
value := Data[i,j+1];
|
|
|
|
if (LoopIt = 2) and (Code[j] = 'C') then
|
|
|
|
RItem := value;
|
|
|
|
|
|
|
|
if value = 1 then
|
|
|
|
begin
|
|
|
|
if ((Tot[i]+RItem >= Lbounds[k]) and (Tot[i]+RItem <= Ubounds[k])) then
|
|
|
|
begin
|
|
|
|
if subjgrp = 1 then
|
|
|
|
begin
|
|
|
|
RMHRight[k,j] := RMHRight[k,j] + 1;
|
|
|
|
RScrGrpCnt[k,j] := RScrGrpCnt[k,j] + 1;
|
|
|
|
end; // if reference group
|
|
|
|
if subjgrp = 2 then
|
|
|
|
begin
|
|
|
|
FMHRight[k,j] := FMHRight[k,j] + 1;
|
|
|
|
FScrGrpCnt[k,j] := FScrGrpCnt[k,j] + 1;
|
|
|
|
end; // if focus group
|
|
|
|
end; // end if () and ()
|
|
|
|
end; // value = 1
|
|
|
|
|
|
|
|
if value = 0 then
|
|
|
|
begin
|
|
|
|
if ((Tot[i]+RItem >= Lbounds[k]) and (Tot[i]+RItem <= Ubounds[k])) then
|
|
|
|
begin
|
|
|
|
if subjgrp = 1 then
|
|
|
|
begin
|
|
|
|
RMHWrong[k,j] := RMHWrong[k,j] + 1;
|
|
|
|
RScrGrpCnt[k,j] := RScrGrpCnt[k,j] + 1;
|
|
|
|
end;
|
|
|
|
if subjgrp = 2 then
|
|
|
|
begin
|
|
|
|
FMHWrong[k,j] := FMHWrong[k,j] + 1;
|
|
|
|
FScrGrpCnt[k,j] := FScrGrpCnt[k,j] + 1;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end; // if value = 0
|
|
|
|
end; // next j
|
|
|
|
end; // next i
|
|
|
|
end; // next k
|
|
|
|
|
|
|
|
for j := 0 to NoItems-1 do Code[j] := 'Z'; // clean out ETS code
|
|
|
|
|
|
|
|
// print score group counts for Reference and focus subjects
|
|
|
|
if CountsChk.Checked then
|
|
|
|
begin
|
|
|
|
for i := 0 to nolevels-1 do
|
|
|
|
RowLabels[i] := format('%3d-%3d', [Lbounds[i], Ubounds[i]]);
|
|
|
|
DynIntMatPrint(RScrGrpCnt, nolevels, NoItems, 'Score Level Counts by Item', RowLabels, ColLabels,
|
|
|
|
'Cases in Reference Group', lReport);
|
|
|
|
DynIntMatPrint(FScrGrpCnt, nolevels, NoItems, 'Score Level Counts by Item', RowLabels, ColLabels,
|
|
|
|
'Cases in Focus Group', lReport);
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Plot Item curves if checked
|
|
|
|
if CurvesChk.Checked and (LoopIt = 1) then
|
|
|
|
ItemCurves;
|
|
|
|
|
|
|
|
// check for minimum of 10 per category in each item
|
|
|
|
// compute NT
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
begin
|
|
|
|
for k := 0 to nolevels-1 do
|
|
|
|
begin
|
|
|
|
if ((RScrGrpCnt[k,j] < 10) or (FScrGrpCnt[k,j] < 10)) then
|
|
|
|
Level10OK[k,j] := 0 // insufficient n
|
|
|
|
else
|
|
|
|
Level10OK[k,j] := 1; // 10 or more - OK
|
|
|
|
NT[k,j] := RScrGrpCnt[k,j] + FScrGrpCnt[k,j];
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
for k := 0 to nolevels-1 do
|
|
|
|
begin
|
|
|
|
if Level10OK[k, 0] = 0 then
|
|
|
|
lReport.Add('Insufficient data found in level: %3d - %3d', [Lbounds[k], Ubounds[k]]);
|
|
|
|
end;
|
|
|
|
|
|
|
|
// compute alpha
|
|
|
|
for j := 0 to NoItems - 1 do
|
|
|
|
begin
|
|
|
|
for k := 0 to nolevels-1 do
|
|
|
|
begin
|
|
|
|
if Level10OK[k,j] = 1 then
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
AlphaNum[j] := AlphaNum[j] + (RMHRight[k,j] * FMHWrong[k,j]) / NT[k,j];
|
|
|
|
AlphaDen[j] := AlphaDen[j] + (RMHWrong[k,j] * FMHRight[k,j]) / NT[k,j];
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
2020-04-21 20:00:11 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
begin
|
|
|
|
if AlphaDen[j] = 0.0 then
|
|
|
|
begin
|
|
|
|
MessageDlg(Format('Window too small at item %d level %d', [j+1, k+1]), mtError, [mbOK], 0);
|
|
|
|
exit;
|
|
|
|
end else
|
|
|
|
begin
|
|
|
|
Alpha[j] := AlphaNum[j] / AlphaDen[j];
|
|
|
|
MHDiff[j] := -2.35 * ln(Alpha[j]);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// compute expected values
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
begin
|
|
|
|
for k := 0 to nolevels-1 do
|
|
|
|
if Level10OK[k,j] = 1 then
|
|
|
|
ExpA[k,j] := (RScrGrpCnt[k,j] * (RMHRight[k,j] + FMHRight[k,j] )) / NT[k,j];
|
|
|
|
end;
|
|
|
|
|
|
|
|
// compute variances
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
for k := 0 to nolevels-1 do
|
|
|
|
if Level10OK[k,j] = 1 then
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
Rtm := RMHRight[k,j] + FMHRight[k,j];
|
|
|
|
Wtm := RMHWrong[k,j] + FMHWrong[k,j];
|
|
|
|
VarA[k,j] := (RScrGrpCnt[k,j] * FScrGrpCnt[k,j] * Rtm * Wtm) / (NT[k,j] * NT[k,j] * (NT[k,j]-1));
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
// compute chi-squares
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
begin
|
|
|
|
SumA[j] := 0.0;
|
|
|
|
SumExpA[j] := 0.0;
|
|
|
|
SumVarA[j] := 0.0;
|
|
|
|
for k := 0 to nolevels-1 do
|
|
|
|
if Level10OK[k,j] = 1 then
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
SumA[j] := SumA[j] + RMHRight[k,j];
|
|
|
|
SumExpA[j] := SumExpA[j] + ExpA[k,j];
|
|
|
|
SumVarA[j] := SumVarA[j] + VarA[k,j];
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
2020-04-21 20:00:11 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
begin
|
|
|
|
ChiSqr[j] := (sqr((Abs(SumA[j] - SumExpA[j]) - 0.5))) / SumVarA[j];
|
|
|
|
Prob[j] := 1.0 - chisquaredprob(ChiSqr[j],1);
|
|
|
|
if Prob[j] > 0.05 then Aster[j] := '';
|
|
|
|
if Prob[j] <= 0.05 then Aster[j] := '*';
|
|
|
|
if Prob[j] <= 0.01 then Aster[j] := '**';
|
|
|
|
if Prob[j] <= 0.005 then Aster[j] := '***';
|
|
|
|
end;
|
|
|
|
|
|
|
|
// compute std. errors
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
begin
|
|
|
|
C[j] := 0.0;
|
|
|
|
for k := 0 to nolevels-1 do
|
|
|
|
if Level10OK[k,j] = 1 then
|
|
|
|
C[j] := C[j] + ((RMHRight[k,j] * FMHWrong[k,j]) / NT[k,j]);
|
|
|
|
end;
|
|
|
|
|
|
|
|
for j := 0 to NoItems - 1 do
|
|
|
|
begin
|
|
|
|
SEMHDDif[j] := 0.0;
|
|
|
|
for k := 0 to nolevels-1 do
|
|
|
|
begin
|
|
|
|
if Level10OK[k,j] = 1 then
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
SEMHDDif[j] := SEMHDDif[j]
|
|
|
|
+ ( (RMHRight[k,j] * FMHWrong[k,j] ) + ( Alpha[j] * RMHWrong[k,j] * FMHRight[k,j]))
|
|
|
|
* ( RMHRight[k,j] + FMHWrong[k,j] + Alpha[j] * ( RMHWrong[k,j] + FMHRight[k,j] )) / ( 2.0 * NT[k,j] * NT[k,j]);
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
2020-04-21 20:00:11 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
SEMHDDif[j] := (2.35 / C[j]) * sqrt(SEMHDDif[j]);
|
|
|
|
|
|
|
|
// code results with ETS codes
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
begin
|
|
|
|
if ( (abs(MHDiff[j]) > 1.5) and ((abs(MHDiff[j]) - (1.96 * SEMHDDif[j]) > 1.0))) then
|
|
|
|
Code[j] := 'C';
|
|
|
|
if ((abs(MHDiff[j]) - (1.96 * SEMHDDif[j]) <= 0.0) or (abs(MHDiff[j]) <= 1.0)) then
|
|
|
|
Code[j] := 'A';
|
|
|
|
if ((code[j] <> 'A') and (code[j] <> 'C')) then
|
|
|
|
Code[j] := 'B';
|
|
|
|
end;
|
|
|
|
|
|
|
|
// purge
|
|
|
|
TotPurge := 0;
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
begin
|
|
|
|
if (code[j] = 'C') then
|
|
|
|
begin
|
|
|
|
TotPurge := TotPurge + 1;
|
|
|
|
for i := 0 to NoCases - 1 do Tot[i] := Tot[i] - Data[i,j+1];
|
|
|
|
if Alpha[j] > 1.0 then CodeRF[j] := 'R';
|
|
|
|
if Alpha[j] < 1.0 then CodeRF[j] := 'F';
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// show results
|
|
|
|
lReport.Add(
|
|
|
|
'CODES ITEM SIG. ALPHA CHI2 P-VALUE MH D-DIF S.E. MH D-DIF');
|
|
|
|
lReport.Add(
|
|
|
|
'----- ---- ---- ------- --------- ------- -------- -------------');
|
|
|
|
for j := 0 to noitems-1 do
|
|
|
|
begin
|
|
|
|
lReport.Add(' %1s %1s %4d %3s %7.3f %9.3f %7.3f %9.3f %9.3f', [
|
|
|
|
code[j],CodeRF[j], j+1, Aster[j],Alpha[j],ChiSqr[j],Prob[j],MHDiff[j], SEMHDDif[j]
|
|
|
|
]);
|
|
|
|
end;
|
|
|
|
lReport.Add('');
|
|
|
|
|
|
|
|
if LoopIt = 1 then
|
|
|
|
begin
|
|
|
|
lReport.Add('No. of items purged in pass 1: %d', [TotPurge]);
|
|
|
|
lReport.Add('Item Numbers:');
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
begin
|
|
|
|
if Code[j] = 'C' then
|
|
|
|
lReport.Add(' %d', [j+1]);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
lReport.Add('');
|
|
|
|
lReport.Add(DIVIDER);
|
|
|
|
lReport.Add('');
|
|
|
|
until LoopIt = 2;
|
|
|
|
|
|
|
|
DisplayReport(lReport);
|
|
|
|
finally
|
|
|
|
lReport.Free;
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDIFFrm.HelpBtnClick(Sender: TObject);
|
|
|
|
begin
|
|
|
|
if ContextHelpForm = nil then
|
|
|
|
Application.CreateForm(TContextHelpForm, ContextHelpForm);
|
|
|
|
ContextHelpForm.HelpMessage((Sender as TButton).Tag);
|
|
|
|
end;
|
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
procedure TDIFFrm.UpBoundEditEditingDone(Sender: TObject);
|
|
|
|
var
|
|
|
|
level: Integer;
|
|
|
|
n: Integer;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
level := LevelScroll.Position;
|
|
|
|
if TryStrToInt(UpBoundEdit.Text, n) then
|
|
|
|
begin
|
|
|
|
UBounds[level] := n;
|
|
|
|
if level + 1 = StrToInt(LevelsEdit.Text) then
|
|
|
|
exit;
|
|
|
|
if LBounds[level+1] = -1 then
|
|
|
|
LBounds[level+1] := UBounds[level] + 1;
|
|
|
|
end else
|
|
|
|
MessageDlg(Format('No valid number in upper bound at level #%d.', [level+1]), mtError, [mbOK], 0);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDIFFrm.VarListSelectionChange(Sender: TObject; User: boolean);
|
|
|
|
begin
|
|
|
|
UpdateBtnStates;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TDIFFrm.AllBtnClick(Sender: TObject);
|
2020-04-21 20:00:11 +00:00
|
|
|
var
|
|
|
|
i: integer;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
if VarList.Items.Count > 0 then
|
|
|
|
begin
|
|
|
|
for i := 0 to VarList.Items.Count - 1 do
|
|
|
|
ItemsList.Items.Add(VarList.Items[i]);
|
|
|
|
VarList.Clear;
|
|
|
|
UpdateBtnStates;
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
procedure TDIFfrm.AlphaRel(AReport: TStrings);
|
2020-03-30 18:01:44 +00:00
|
|
|
var
|
2020-04-21 20:00:11 +00:00
|
|
|
i: integer;
|
|
|
|
Alpha1, SEMeas: double;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
Alpha1 := 0.0;
|
|
|
|
for i := 0 to NoItems-1 do
|
|
|
|
Alpha1 := Alpha1 + variances[i]; // sum of item variances
|
|
|
|
Alpha1 := Alpha1 / tvar;
|
|
|
|
Alpha1 := 1.0 - Alpha1;
|
|
|
|
Alpha1 := (NoItems / (NoItems - 1.0)) * Alpha1;
|
|
|
|
|
|
|
|
SEMeas := tsd * sqrt(1.0 - Alpha1);
|
|
|
|
AReport.Add('Alpha Reliability Estimate for Test: %8.4f', [Alpha1]);
|
|
|
|
AReport.Add('S.E. of Measurement: %8.4f', [SEMeas]);
|
|
|
|
AReport.Add('');
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
procedure TDIFfrm.ItemCorrs(AReport: TStrings);
|
2020-03-30 18:01:44 +00:00
|
|
|
var
|
2020-04-21 20:00:11 +00:00
|
|
|
i, j, k: integer;
|
|
|
|
title: string;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
// cross-products
|
|
|
|
for i := 0 to NoItems-1 do
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
for k := 0 to NoCases-1 do
|
|
|
|
CorMat[i,j] := CorMat[i,j] + (Data[k,i+1] * Data[k,j+1]);
|
|
|
|
|
|
|
|
// covariances
|
|
|
|
for i := 0 to NoItems-1 do
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
CorMat[i,j] := (CorMat[i,j] - (NoCases * Means[i] * Means[j])) / (NoCases-1);
|
|
|
|
|
|
|
|
// correlations
|
|
|
|
for i := 0 to NoItems-1 do
|
|
|
|
for j := 0 to NoItems-1 do
|
|
|
|
CorMat[i,j] := CorMat[i,j] / (StdDevs[i] * StdDevs[j]);
|
|
|
|
|
|
|
|
// show results
|
|
|
|
title := 'Correlations Among Items';
|
|
|
|
MatPrint(CorMat, NoItems, NoItems, title, RowLabels, ColLabels, NoCases, AReport);
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
procedure TDIFfrm.ItemTestCorrs(AReport: TStrings);
|
|
|
|
var
|
|
|
|
i, j: integer;
|
|
|
|
Cors: DblDyneVec;
|
|
|
|
title: string;
|
|
|
|
begin
|
|
|
|
SetLength(Cors,NoItems);
|
|
|
|
|
|
|
|
// cross-products
|
|
|
|
for i := 0 to NoItems-1 do
|
|
|
|
for j := 0 to NoCases-1 do
|
|
|
|
Cors[i] := Cors[i] + (Data[j,i+1] * Tot[j]);
|
|
|
|
|
|
|
|
// covariances
|
|
|
|
for i := 0 to NoItems-1 do
|
|
|
|
Cors[i] := (Cors[i] - (NoCases * Means[i] * tmean)) / (NoCases-1);
|
|
|
|
|
|
|
|
// correlations
|
|
|
|
for i := 0 to NoItems-1 do
|
|
|
|
Cors[i] := Cors[i] / (StdDevs[i] * tsd);
|
|
|
|
|
|
|
|
// show results
|
|
|
|
title := 'Item-Total Correlations';
|
|
|
|
DynVectorPrint(Cors, NoItems, title, ColLabels, NoCases, AReport);
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
// release memory
|
|
|
|
Cors := nil;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
procedure TDIFfrm.ItemCurves;
|
2020-03-30 18:01:44 +00:00
|
|
|
var
|
2020-04-21 20:00:11 +00:00
|
|
|
i, j: integer;
|
|
|
|
XPlotPts: DblDyneMat;
|
|
|
|
YPlotPts: DblDyneMat;
|
|
|
|
max: integer;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
SetLength(XPlotPts, 1, nolevels);
|
|
|
|
SetLength(YPlotPts, 2, nolevels);
|
|
|
|
|
|
|
|
// get maximum no. of scores in either groups bins
|
|
|
|
for i := 0 to NoItems-1 do
|
|
|
|
begin
|
|
|
|
max := 0;
|
|
|
|
for j := 0 to nolevels-1 do
|
|
|
|
begin
|
|
|
|
if RMHRight[j,i] > max then max := RMHRight[j,i];
|
|
|
|
if FMHRight[j,i] > max then max := FMHRight[j,i];
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Plot reference group in blue, focus group in red
|
|
|
|
for j := 0 to nolevels-1 do //get points to plot
|
|
|
|
begin
|
|
|
|
XPlotPts[0, j] := Lbounds[j];
|
|
|
|
YPlotPts[0, j] := RMHRight[j,i];
|
|
|
|
YPlotPts[1, j] := FMHRight[j,i];
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Plot the points
|
|
|
|
GraphFrm.BackColor := clWhite;
|
|
|
|
GraphFrm.ShowLeftWall := true;
|
|
|
|
GraphFrm.ShowRightWall := true;
|
|
|
|
GraphFrm.ShowBottomWall := true;
|
|
|
|
GraphFrm.ShowBackWall := true;
|
|
|
|
GraphFrm.BackColor := GRAPH_BACK_COLOR;
|
|
|
|
GraphFrm.WallColor := GRAPH_WALL_COLOR;
|
|
|
|
GraphFrm.FloorColor := GRAPH_FLOOR_COLOR;
|
|
|
|
GraphFrm.Heading := Format('Blue: Reference, Red: Focus for item %d', [i+1]);
|
|
|
|
GraphFrm.XTitle := 'Lower bounds of levels';
|
|
|
|
GraphFrm.YTitle := 'Frequencies';
|
|
|
|
GraphFrm.nosets := 2;
|
|
|
|
GraphFrm.nbars := nolevels;
|
|
|
|
GraphFrm.barwideprop := 0.5;
|
|
|
|
GraphFrm.miny := 0.0;
|
|
|
|
GraphFrm.maxy := max;
|
|
|
|
GraphFrm.AutoScaled := false;
|
|
|
|
GraphFrm.GraphType := 5; // 2d line charts
|
|
|
|
GraphFrm.PtLabels := false;
|
|
|
|
GraphFrm.SetLabels[1] := 'Reference';
|
|
|
|
GraphFrm.SetLabels[2] := 'Focus';
|
|
|
|
GraphFrm.Ypoints := YPlotPts;
|
|
|
|
GraphFrm.Xpoints := XPlotPts;
|
|
|
|
|
|
|
|
if (GraphFrm.ShowModal <> mrOK) then
|
|
|
|
exit;
|
|
|
|
end; // next item
|
|
|
|
|
|
|
|
XPlotPts := nil;
|
|
|
|
YPlotPts := nil;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
procedure TDIFfrm.UpdateBtnStates;
|
2020-03-30 18:01:44 +00:00
|
|
|
var
|
2020-04-21 20:00:11 +00:00
|
|
|
lSelected: Boolean;
|
2020-03-30 18:01:44 +00:00
|
|
|
begin
|
2020-04-21 20:00:11 +00:00
|
|
|
lSelected := AnySelected(VarList);
|
|
|
|
ItemInBtn.Enabled := lSelected;
|
|
|
|
GrpInBtn.Enabled := lSelected;
|
|
|
|
ItemOutBtn.Enabled := AnySelected(ItemsList) and (GroupVarEdit.Text = '');
|
|
|
|
GrpOutBtn.Enabled := GroupVarEdit.Text <> '';
|
|
|
|
AllBtn.Enabled := VarList.Items.Count > 0;
|
|
|
|
end;
|
2020-03-30 18:01:44 +00:00
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
function TDIFFrm.ValidNumEdit(AEdit: TEdit; AName: String;
|
|
|
|
out AValue: Integer): Boolean;
|
|
|
|
begin
|
|
|
|
Result := false;
|
|
|
|
if AEdit.Text = '' then
|
|
|
|
begin
|
|
|
|
AEdit.SetFocus;
|
|
|
|
MessageDlg(AName + ' not specified.', mtError, [mbOK], 0);
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
if not TryStrToInt(AEdit.Text, AValue) then
|
|
|
|
begin
|
|
|
|
AEdit.SetFocus;
|
|
|
|
MessageDlg(AName + ' is not a valid number.', mtError, [mbOk], 0);
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
Result := true;
|
2020-03-30 18:01:44 +00:00
|
|
|
end;
|
|
|
|
|
2020-04-21 20:00:11 +00:00
|
|
|
|
2020-03-30 18:01:44 +00:00
|
|
|
initialization
|
|
|
|
{$I difunit.lrs}
|
|
|
|
|
|
|
|
end.
|
|
|
|
|