From 7daa8215e6cd8931e6f1a62b15d38164aaec4c89 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Sun, 9 May 2021 16:52:18 +0000 Subject: [PATCH] LazStats: Inherit KappaUnit from BasicStatsReportFormUnit. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8016 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- applications/lazstats/source/LazStats.lpi | 2 +- .../analysis/nonparametric/kappaunit.lfm | 788 ++++++------ .../analysis/nonparametric/kappaunit.pas | 1132 ++++++++--------- .../lazstats/source/forms/mainunit.pas | 6 +- 4 files changed, 893 insertions(+), 1035 deletions(-) diff --git a/applications/lazstats/source/LazStats.lpi b/applications/lazstats/source/LazStats.lpi index c4229802b..8b7238010 100644 --- a/applications/lazstats/source/LazStats.lpi +++ b/applications/lazstats/source/LazStats.lpi @@ -926,7 +926,7 @@ - + diff --git a/applications/lazstats/source/forms/analysis/nonparametric/kappaunit.lfm b/applications/lazstats/source/forms/analysis/nonparametric/kappaunit.lfm index 726ce6bca..4cf8408fc 100644 --- a/applications/lazstats/source/forms/analysis/nonparametric/kappaunit.lfm +++ b/applications/lazstats/source/forms/analysis/nonparametric/kappaunit.lfm @@ -1,442 +1,384 @@ -object WeightedKappaFrm: TWeightedKappaFrm +inherited WeightedKappaForm: TWeightedKappaForm Left = 448 - Height = 560 + Height = 559 Top = 184 - Width = 539 + Width = 1086 HelpType = htKeyword HelpKeyword = 'html/KappaandWeightedKappa.htm' - AutoSize = True Caption = 'Kappa and Weighted Kappa' - ClientHeight = 560 - ClientWidth = 539 - OnActivate = FormActivate + ClientHeight = 559 + ClientWidth = 1086 OnCreate = FormCreate - OnShow = FormShow - Position = poMainFormCenter - LCLVersion = '2.1.0.0' - object InputGroup: TRadioGroup - AnchorSideLeft.Control = Owner - AnchorSideTop.Control = Owner - AnchorSideRight.Control = Owner - AnchorSideRight.Side = asrBottom - Left = 8 - Height = 89 - Top = 8 + inherited ParamsPanel: TPanel + Height = 543 Width = 488 - Align = alCustom - AutoFill = True AutoSize = True - BorderSpacing.Left = 8 - BorderSpacing.Top = 8 - BorderSpacing.Right = 8 - Caption = 'Input Options:' - ChildSizing.LeftRightSpacing = 6 - ChildSizing.TopBottomSpacing = 6 - ChildSizing.EnlargeHorizontal = crsHomogenousChildResize - ChildSizing.EnlargeVertical = crsHomogenousChildResize - ChildSizing.ShrinkHorizontal = crsScaleChilds - ChildSizing.ShrinkVertical = crsScaleChilds - ChildSizing.Layout = cclLeftToRightThenTopToBottom - ChildSizing.ControlsPerLine = 1 - ClientHeight = 69 - ClientWidth = 484 - Items.Strings = ( - 'Count cases classified by row and column variables in the data grid' - 'Use frequencies in a variable for a given row variable value and column variable value.' - 'Use proportions in a variable for a given row variable value and column variable value.' - ) - OnClick = InputGroupClick - TabOrder = 0 - end - object Label1: TLabel - AnchorSideLeft.Control = Owner - AnchorSideTop.Control = InputGroup - AnchorSideTop.Side = asrBottom - Left = 8 - Height = 15 - Top = 105 - Width = 46 - BorderSpacing.Left = 8 - BorderSpacing.Top = 8 - Caption = 'Variables' - ParentColor = False - end - object VarList: TListBox - AnchorSideLeft.Control = Owner - AnchorSideTop.Control = Label1 - AnchorSideTop.Side = asrBottom - AnchorSideRight.Control = RowIn - AnchorSideBottom.Control = NCasesEdit - Left = 8 - Height = 358 - Top = 122 - Width = 265 - Anchors = [akTop, akLeft, akRight, akBottom] - BorderSpacing.Left = 8 - BorderSpacing.Top = 2 - BorderSpacing.Right = 8 - ItemHeight = 0 - OnSelectionChange = VarListSelectionChange - TabOrder = 1 - end - object NCasesLbl: TLabel - AnchorSideLeft.Control = VarList - AnchorSideTop.Control = NCasesEdit - AnchorSideTop.Side = asrCenter - AnchorSideRight.Control = NCasesEdit - Left = 8 - Height = 15 - Top = 492 - Width = 122 - BorderSpacing.Right = 8 - Caption = 'Total Number of Cases:' - ParentColor = False - end - object NCasesEdit: TEdit - AnchorSideLeft.Control = NCasesLbl - AnchorSideLeft.Side = asrBottom - AnchorSideTop.Side = asrBottom - AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = Bevel1 - Left = 138 - Height = 23 - Top = 488 - Width = 54 - Alignment = taRightJustify - Anchors = [akLeft, akBottom] - BorderSpacing.Top = 8 - BorderSpacing.Bottom = 8 - TabOrder = 2 - Text = 'NCasesEdit' - end - object RowIn: TBitBtn - AnchorSideLeft.Control = OptionsGroup - AnchorSideTop.Control = VarList - Left = 281 - Height = 28 - Top = 122 - Width = 28 - Images = MainDataModule.ImageList - ImageIndex = 1 - OnClick = RowInClick - Spacing = 0 - TabOrder = 3 - end - object RowOut: TBitBtn - AnchorSideLeft.Control = OptionsGroup - AnchorSideTop.Control = RowIn - AnchorSideTop.Side = asrBottom - Left = 281 - Height = 28 - Top = 154 - Width = 28 - BorderSpacing.Top = 4 - Images = MainDataModule.ImageList - ImageIndex = 0 - OnClick = RowOutClick - Spacing = 0 - TabOrder = 4 - end - object ColIn: TBitBtn - AnchorSideLeft.Control = OptionsGroup - AnchorSideTop.Control = RowOut - AnchorSideTop.Side = asrBottom - Left = 281 - Height = 28 - Top = 202 - Width = 28 - BorderSpacing.Top = 20 - Images = MainDataModule.ImageList - ImageIndex = 1 - OnClick = ColInClick - Spacing = 0 - TabOrder = 6 - end - object ColOut: TBitBtn - AnchorSideLeft.Control = OptionsGroup - AnchorSideTop.Control = ColIn - AnchorSideTop.Side = asrBottom - Left = 281 - Height = 28 - Top = 234 - Width = 28 - BorderSpacing.Top = 4 - Images = MainDataModule.ImageList - ImageIndex = 0 - OnClick = ColOutClick - Spacing = 0 - TabOrder = 7 - end - object DepIn: TBitBtn - AnchorSideLeft.Control = OptionsGroup - AnchorSideTop.Control = ColOut - AnchorSideTop.Side = asrBottom - Left = 281 - Height = 28 - Top = 282 - Width = 28 - BorderSpacing.Top = 20 - Images = MainDataModule.ImageList - ImageIndex = 1 - OnClick = DepInClick - Spacing = 0 - TabOrder = 9 - end - object DepOut: TBitBtn - AnchorSideLeft.Control = OptionsGroup - AnchorSideTop.Control = DepIn - AnchorSideTop.Side = asrBottom - Left = 281 - Height = 28 - Top = 314 - Width = 28 - BorderSpacing.Top = 4 - Images = MainDataModule.ImageList - ImageIndex = 0 - OnClick = DepOutClick - Spacing = 0 - TabOrder = 10 - end - object Label2: TLabel - AnchorSideLeft.Control = RaterAEdit - AnchorSideBottom.Control = RaterAEdit - Left = 317 - Height = 15 - Top = 130 - Width = 38 - Anchors = [akLeft, akBottom] - BorderSpacing.Bottom = 2 - Caption = 'Rater A' - ParentColor = False - end - object Label3: TLabel - AnchorSideLeft.Control = RaterBEdit - AnchorSideBottom.Control = RaterBEdit - Left = 317 - Height = 15 - Top = 210 - Width = 37 - Anchors = [akLeft, akBottom] - BorderSpacing.Bottom = 2 - Caption = 'Rater B' - ParentColor = False - end - object DepLbl: TLabel - AnchorSideLeft.Control = DepEdit - AnchorSideBottom.Control = DepEdit - Left = 317 - Height = 15 - Top = 290 - Width = 99 - Anchors = [akLeft, akBottom] - BorderSpacing.Bottom = 2 - Caption = 'Variable to Analyze' - ParentColor = False - end - object RaterAEdit: TEdit - AnchorSideLeft.Control = RowIn - AnchorSideLeft.Side = asrBottom - AnchorSideRight.Control = OptionsGroup - AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = RowOut - AnchorSideBottom.Side = asrBottom - Left = 317 - Height = 23 - Top = 147 - Width = 214 - Anchors = [akLeft, akRight, akBottom] - BorderSpacing.Left = 8 - BorderSpacing.Bottom = 12 - ReadOnly = True - TabOrder = 5 - Text = 'RaterAEdit' - end - object RaterBEdit: TEdit - AnchorSideLeft.Control = ColIn - AnchorSideLeft.Side = asrBottom - AnchorSideRight.Control = OptionsGroup - AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = ColOut - AnchorSideBottom.Side = asrBottom - Left = 317 - Height = 23 - Top = 227 - Width = 214 - Anchors = [akLeft, akRight, akBottom] - BorderSpacing.Left = 8 - BorderSpacing.Bottom = 12 - ReadOnly = True - TabOrder = 8 - Text = 'RaterBEdit' - end - object DepEdit: TEdit - AnchorSideLeft.Control = DepOut - AnchorSideLeft.Side = asrBottom - AnchorSideRight.Control = OptionsGroup - AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = DepOut - AnchorSideBottom.Side = asrBottom - Left = 317 - Height = 23 - Top = 307 - Width = 214 - Anchors = [akLeft, akRight, akBottom] - BorderSpacing.Left = 8 - BorderSpacing.Bottom = 12 - ReadOnly = True - TabOrder = 11 - Text = 'DepEdit' - end - object OptionsGroup: TGroupBox - AnchorSideLeft.Control = RowIn - AnchorSideTop.Control = DepOut - AnchorSideTop.Side = asrBottom - AnchorSideRight.Control = Owner - AnchorSideRight.Side = asrBottom - Left = 281 - Height = 156 - Top = 354 - Width = 250 - Anchors = [akTop, akRight] - AutoSize = True - BorderSpacing.Top = 12 - BorderSpacing.Right = 8 - Caption = 'Options:' - ChildSizing.LeftRightSpacing = 12 - ChildSizing.TopBottomSpacing = 6 - ChildSizing.VerticalSpacing = 2 - ChildSizing.Layout = cclLeftToRightThenTopToBottom - ChildSizing.ControlsPerLine = 1 - ClientHeight = 136 - ClientWidth = 246 - TabOrder = 12 - object ObsChk: TCheckBox - Left = 12 - Height = 19 - Top = 6 - Width = 222 - Caption = 'Show Observed Frequencies' - TabOrder = 0 + ClientHeight = 543 + ClientWidth = 488 + inherited CloseBtn: TButton + Left = 433 + Top = 518 end - object ExpChk: TCheckBox - Left = 12 - Height = 19 - Top = 27 - Width = 222 - Caption = 'Show Expected Frequencies' - TabOrder = 1 + inherited ComputeBtn: TButton + Left = 349 + Top = 518 end - object PropChk: TCheckBox - Left = 12 - Height = 19 - Top = 48 - Width = 222 - Caption = 'Show Row and Column Proportions' - TabOrder = 2 + inherited ResetBtn: TButton + Left = 287 + Top = 518 end - object ChiChk: TCheckBox - Left = 12 - Height = 19 - Top = 69 - Width = 222 - Caption = 'Show Cell Chi Square Values' - TabOrder = 3 + inherited HelpBtn: TButton + Tag = 128 + Left = 228 + Top = 518 end - object YatesChk: TCheckBox - Left = 12 - Height = 19 - Top = 90 - Width = 222 - Caption = 'Use Yates'' Correction for a 2 by 2 table' + inherited ButtonBevel: TBevel + Top = 502 + Width = 488 + end + object InputGroup: TRadioGroup[5] + AnchorSideLeft.Control = ParamsPanel + AnchorSideTop.Control = ParamsPanel + AnchorSideRight.Control = ParamsPanel + AnchorSideRight.Side = asrBottom + Left = 0 + Height = 89 + Top = 0 + Width = 488 + Align = alCustom + Anchors = [akTop, akLeft, akRight] + AutoFill = True + AutoSize = True + Caption = 'Input Options:' + ChildSizing.LeftRightSpacing = 6 + ChildSizing.TopBottomSpacing = 6 + ChildSizing.EnlargeHorizontal = crsHomogenousChildResize + ChildSizing.EnlargeVertical = crsHomogenousChildResize + ChildSizing.ShrinkHorizontal = crsScaleChilds + ChildSizing.ShrinkVertical = crsScaleChilds + ChildSizing.Layout = cclLeftToRightThenTopToBottom + ChildSizing.ControlsPerLine = 1 + ClientHeight = 69 + ClientWidth = 484 + Items.Strings = ( + 'Count cases classified by row and column variables in the data grid' + 'Use frequencies in a variable for a given row variable value and column variable value.' + 'Use proportions in a variable for a given row variable value and column variable value.' + ) + OnClick = InputGroupClick TabOrder = 4 end - object SaveChk: TCheckBox - Left = 12 - Height = 19 - Top = 111 - Width = 222 - Caption = 'Save as a File of Frequency Data' + object Label1: TLabel[6] + AnchorSideLeft.Control = ParamsPanel + AnchorSideTop.Control = InputGroup + AnchorSideTop.Side = asrBottom + Left = 0 + Height = 15 + Top = 97 + Width = 46 + BorderSpacing.Top = 8 + Caption = 'Variables' + ParentColor = False + end + object VarList: TListBox[7] + AnchorSideLeft.Control = ParamsPanel + AnchorSideTop.Control = Label1 + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = RowIn + AnchorSideBottom.Control = NCasesEdit + Left = 0 + Height = 357 + Top = 114 + Width = 230 + Anchors = [akTop, akLeft, akRight, akBottom] + BorderSpacing.Top = 2 + BorderSpacing.Right = 8 + ItemHeight = 0 + OnDblClick = VarListDblClick + OnSelectionChange = VarListSelectionChange TabOrder = 5 end + object RowIn: TBitBtn[8] + AnchorSideLeft.Control = OptionsGroup + AnchorSideTop.Control = VarList + Left = 238 + Height = 28 + Top = 114 + Width = 28 + Images = MainDataModule.ImageList + ImageIndex = 1 + OnClick = RowInClick + Spacing = 0 + TabOrder = 6 + end + object RowOut: TBitBtn[9] + AnchorSideLeft.Control = OptionsGroup + AnchorSideTop.Control = RowIn + AnchorSideTop.Side = asrBottom + Left = 238 + Height = 28 + Top = 146 + Width = 28 + BorderSpacing.Top = 4 + Images = MainDataModule.ImageList + ImageIndex = 0 + OnClick = RowOutClick + Spacing = 0 + TabOrder = 7 + end + object ColIn: TBitBtn[10] + AnchorSideLeft.Control = OptionsGroup + AnchorSideTop.Control = RowOut + AnchorSideTop.Side = asrBottom + Left = 238 + Height = 28 + Top = 194 + Width = 28 + BorderSpacing.Top = 20 + Images = MainDataModule.ImageList + ImageIndex = 1 + OnClick = ColInClick + Spacing = 0 + TabOrder = 8 + end + object ColOut: TBitBtn[11] + AnchorSideLeft.Control = OptionsGroup + AnchorSideTop.Control = ColIn + AnchorSideTop.Side = asrBottom + Left = 238 + Height = 28 + Top = 226 + Width = 28 + BorderSpacing.Top = 4 + Images = MainDataModule.ImageList + ImageIndex = 0 + OnClick = ColOutClick + Spacing = 0 + TabOrder = 9 + end + object DepIn: TBitBtn[12] + AnchorSideLeft.Control = OptionsGroup + AnchorSideTop.Control = ColOut + AnchorSideTop.Side = asrBottom + Left = 238 + Height = 28 + Top = 274 + Width = 28 + BorderSpacing.Top = 20 + Images = MainDataModule.ImageList + ImageIndex = 1 + OnClick = DepInClick + Spacing = 0 + TabOrder = 10 + end + object DepOut: TBitBtn[13] + AnchorSideLeft.Control = OptionsGroup + AnchorSideTop.Control = DepIn + AnchorSideTop.Side = asrBottom + Left = 238 + Height = 28 + Top = 306 + Width = 28 + BorderSpacing.Top = 4 + Images = MainDataModule.ImageList + ImageIndex = 0 + OnClick = DepOutClick + Spacing = 0 + TabOrder = 11 + end + object Label2: TLabel[14] + AnchorSideLeft.Control = RaterAEdit + AnchorSideBottom.Control = RaterAEdit + Left = 274 + Height = 15 + Top = 122 + Width = 38 + Anchors = [akLeft, akBottom] + BorderSpacing.Bottom = 2 + Caption = 'Rater A' + ParentColor = False + end + object Label3: TLabel[15] + AnchorSideLeft.Control = RaterBEdit + AnchorSideBottom.Control = RaterBEdit + Left = 274 + Height = 15 + Top = 202 + Width = 37 + Anchors = [akLeft, akBottom] + BorderSpacing.Bottom = 2 + Caption = 'Rater B' + ParentColor = False + end + object DepLbl: TLabel[16] + AnchorSideLeft.Control = DepEdit + AnchorSideBottom.Control = DepEdit + Left = 274 + Height = 15 + Top = 282 + Width = 99 + Anchors = [akLeft, akBottom] + BorderSpacing.Bottom = 2 + Caption = 'Variable to Analyze' + ParentColor = False + end + object RaterAEdit: TEdit[17] + AnchorSideLeft.Control = RowIn + AnchorSideLeft.Side = asrBottom + AnchorSideRight.Control = ParamsPanel + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = RowOut + AnchorSideBottom.Side = asrBottom + Left = 274 + Height = 23 + Top = 139 + Width = 214 + Anchors = [akLeft, akRight, akBottom] + BorderSpacing.Left = 8 + BorderSpacing.Bottom = 12 + ReadOnly = True + TabOrder = 12 + Text = 'RaterAEdit' + end + object RaterBEdit: TEdit[18] + AnchorSideLeft.Control = ColIn + AnchorSideLeft.Side = asrBottom + AnchorSideRight.Control = ParamsPanel + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = ColOut + AnchorSideBottom.Side = asrBottom + Left = 274 + Height = 23 + Top = 219 + Width = 214 + Anchors = [akLeft, akRight, akBottom] + BorderSpacing.Left = 8 + BorderSpacing.Bottom = 12 + ReadOnly = True + TabOrder = 13 + Text = 'RaterBEdit' + end + object DepEdit: TEdit[19] + AnchorSideLeft.Control = DepOut + AnchorSideLeft.Side = asrBottom + AnchorSideRight.Control = ParamsPanel + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = DepOut + AnchorSideBottom.Side = asrBottom + Left = 274 + Height = 23 + Top = 299 + Width = 214 + Anchors = [akLeft, akRight, akBottom] + BorderSpacing.Left = 8 + BorderSpacing.Bottom = 12 + ReadOnly = True + TabOrder = 14 + Text = 'DepEdit' + end + object OptionsGroup: TGroupBox[20] + AnchorSideLeft.Control = RowIn + AnchorSideTop.Control = DepOut + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = ParamsPanel + AnchorSideRight.Side = asrBottom + Left = 238 + Height = 156 + Top = 346 + Width = 250 + Anchors = [akTop, akRight] + AutoSize = True + BorderSpacing.Top = 12 + Caption = 'Options:' + ChildSizing.LeftRightSpacing = 12 + ChildSizing.TopBottomSpacing = 6 + ChildSizing.VerticalSpacing = 2 + ChildSizing.Layout = cclLeftToRightThenTopToBottom + ChildSizing.ControlsPerLine = 1 + ClientHeight = 136 + ClientWidth = 246 + TabOrder = 15 + object ObsChk: TCheckBox + Left = 12 + Height = 19 + Top = 6 + Width = 222 + Caption = 'Show Observed Frequencies' + TabOrder = 0 + end + object ExpChk: TCheckBox + Left = 12 + Height = 19 + Top = 27 + Width = 222 + Caption = 'Show Expected Frequencies' + TabOrder = 1 + end + object PropChk: TCheckBox + Left = 12 + Height = 19 + Top = 48 + Width = 222 + Caption = 'Show Row and Column Proportions' + TabOrder = 2 + end + object ChiChk: TCheckBox + Left = 12 + Height = 19 + Top = 69 + Width = 222 + Caption = 'Show Cell Chi Square Values' + TabOrder = 3 + end + object YatesChk: TCheckBox + Left = 12 + Height = 19 + Top = 90 + Width = 222 + Caption = 'Use Yates'' Correction for a 2 by 2 table' + TabOrder = 4 + end + object SaveChk: TCheckBox + Left = 12 + Height = 19 + Top = 111 + Width = 222 + Caption = 'Save as a File of Frequency Data' + TabOrder = 5 + end + end + object NCasesLbl: TLabel[21] + AnchorSideLeft.Control = VarList + AnchorSideTop.Control = NCasesEdit + AnchorSideTop.Side = asrCenter + AnchorSideRight.Control = NCasesEdit + Left = 0 + Height = 15 + Top = 483 + Width = 122 + BorderSpacing.Right = 8 + Caption = 'Total Number of Cases:' + ParentColor = False + end + object NCasesEdit: TEdit[22] + AnchorSideLeft.Control = NCasesLbl + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Side = asrBottom + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = ButtonBevel + Left = 130 + Height = 23 + Top = 479 + Width = 54 + Alignment = taRightJustify + Anchors = [akLeft, akBottom] + BorderSpacing.Top = 8 + TabOrder = 16 + Text = 'NCasesEdit' + end end - object ResetBtn: TButton - AnchorSideTop.Control = CloseBtn - AnchorSideRight.Control = ComputeBtn - AnchorSideBottom.Side = asrBottom - Left = 330 - Height = 25 - Top = 527 - Width = 54 - Anchors = [akTop, akRight] - AutoSize = True - BorderSpacing.Left = 8 - BorderSpacing.Right = 8 - BorderSpacing.Bottom = 8 - Caption = 'Reset' - OnClick = ResetBtnClick - TabOrder = 13 - end - object ComputeBtn: TButton - AnchorSideTop.Control = CloseBtn - AnchorSideRight.Control = CloseBtn - AnchorSideBottom.Side = asrBottom - Left = 392 - Height = 25 - Top = 527 - Width = 76 - Anchors = [akTop, akRight] - AutoSize = True - BorderSpacing.Left = 8 - BorderSpacing.Right = 8 - BorderSpacing.Bottom = 8 - Caption = 'Compute' - OnClick = ComputeBtnClick - TabOrder = 14 - end - object CloseBtn: TButton - AnchorSideRight.Control = Owner - AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = Owner - AnchorSideBottom.Side = asrBottom - Left = 476 - Height = 25 - Top = 527 - Width = 55 - Anchors = [akRight, akBottom] - AutoSize = True - BorderSpacing.Left = 8 - BorderSpacing.Right = 8 - BorderSpacing.Bottom = 8 - Caption = 'Close' - ModalResult = 11 - TabOrder = 15 - end - object HelpBtn: TButton - Tag = 128 - AnchorSideTop.Control = CloseBtn - AnchorSideRight.Control = ResetBtn - AnchorSideBottom.Side = asrBottom - Left = 271 - Height = 25 - Top = 527 - Width = 51 - Anchors = [akTop, akRight] - AutoSize = True - BorderSpacing.Left = 8 - BorderSpacing.Right = 8 - BorderSpacing.Bottom = 8 - Caption = 'Help' - OnClick = HelpBtnClick - TabOrder = 16 - end - object Bevel1: TBevel - AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = CloseBtn - Left = 0 - Height = 8 - Top = 519 - Width = 541 - Anchors = [akLeft, akRight, akBottom] - Shape = bsTopLine + inherited ParamsSplitter: TSplitter + Left = 500 + Height = 559 end end diff --git a/applications/lazstats/source/forms/analysis/nonparametric/kappaunit.pas b/applications/lazstats/source/forms/analysis/nonparametric/kappaunit.pas index 5aac28524..d71a48e34 100644 --- a/applications/lazstats/source/forms/analysis/nonparametric/kappaunit.pas +++ b/applications/lazstats/source/forms/analysis/nonparametric/kappaunit.pas @@ -7,19 +7,13 @@ interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, Buttons, - MainUnit, Globals, OutputUnit, FunctionsLib, DictionaryUnit, DataProcs, - MatrixLib, ContextHelpUnit; + MainUnit, FunctionsLib, DictionaryUnit, MatrixLib, BasicStatsReportFormUnit; type - { TWeightedKappaFrm } + { TWeightedKappaForm } - TWeightedKappaFrm = class(TForm) - Bevel1: TBevel; - HelpBtn: TButton; - ResetBtn: TButton; - ComputeBtn: TButton; - CloseBtn: TButton; + TWeightedKappaForm = class(TBasicStatsReportForm) ObsChk: TCheckBox; ExpChk: TCheckBox; PropChk: TCheckBox; @@ -46,177 +40,55 @@ type InputGroup: TRadioGroup; procedure ColInClick(Sender: TObject); procedure ColOutClick(Sender: TObject); - procedure ComputeBtnClick(Sender: TObject); procedure DepInClick(Sender: TObject); procedure DepOutClick(Sender: TObject); - procedure FormActivate(Sender: TObject); procedure FormCreate(Sender: TObject); - procedure FormShow(Sender: TObject); - procedure HelpBtnClick(Sender: TObject); procedure InputGroupClick(Sender: TObject); - procedure ResetBtnClick(Sender: TObject); procedure RowInClick(Sender: TObject); procedure RowOutClick(Sender: TObject); + procedure VarListDblClick(Sender: TObject); procedure VarListSelectionChange(Sender: TObject; {%H-}User: boolean); private { private declarations } - FAutoSized: Boolean; - procedure UpdateBtnStates; + protected + procedure AdjustConstraints; override; + procedure Compute; override; + procedure UpdateBtnStates; override; + function Validate(out AMsg: String; out AControl: TWinControl): Boolean; override; public { public declarations } - end; + procedure Reset; override; + end; var - WeightedKappaFrm: TWeightedKappaFrm; + WeightedKappaForm: TWeightedKappaForm; + implementation +{$R *.lfm} + uses - Math; + Math, + Globals, DataProcs, GridProcs, Utils; -{ TWeightedKappaFrm } +{ TWeightedKappaForm } -procedure TWeightedKappaFrm.ResetBtnClick(Sender: TObject); -var - i: integer; +procedure TWeightedKappaForm.AdjustConstraints; begin - RaterAEdit.Text := ''; - RaterBEdit.Text := ''; - DepEdit.Text := ''; - NCasesEdit.Text := ''; - InputGroup.ItemIndex := 0; - VarList.Clear; - for i := 1 to NoVariables do - VarList.Items.Add(OS3MainFrm.DataGrid.Cells[i,0]); - UpdateBtnStates; + inherited; + + ParamsPanel.Constraints.MinHeight := + OptionsGroup.Top + OptionsGroup.Height + + ButtonBevel.Height + CloseBtn.BorderSpacing.Top + CloseBtn.Height; + + ParamsPanel.Constraints.MinWidth := MaxValueI([ + InputGroup.Width, + OptionsGroup.Width + NCasesEdit.Left + NCasesEdit.Width + VarList.BorderSpacing.Right + ]); end; -procedure TWeightedKappaFrm.RowInClick(Sender: TObject); -var - index: integer; -begin - index := VarList.ItemIndex; - if (index > -1) and (RaterAEdit.Text = '') then - begin - RaterAEdit.Text := VarList.Items[index]; - VarList.Items.Delete(index); - end; - UpdateBtnStates; -end; - -procedure TWeightedKappaFrm.RowOutClick(Sender: TObject); -begin - if RaterAEdit.Text <> '' then - begin - VarList.Items.Add(RaterAEdit.Text); - RaterAEdit.Text := ''; - end; - UpdateBtnStates; -end; - -procedure TWeightedKappaFrm.FormActivate(Sender: TObject); -var - w: Integer; -begin - if FAutoSized then - exit; - - // Autosizing is not working for whatever reason... - //AutoSize := false; - - w := MaxValue([HelpBtn.Width, ResetBtn.Width, ComputeBtn.Width, CloseBtn.Width]); - HelpBtn.Constraints.MinWidth := w; - ResetBtn.Constraints.MinWidth := w; - ComputeBtn.Constraints.MinWidth := w; - CloseBtn.Constraints.MinWidth := w; - - //VarList.Constraints.MinHeight := OptionsGroup.Top + OptionsGroup.Height - NCasesEdit.Height - VarList.BorderSpacing.Bottom; - Constraints.MinWidth := Width; - Constraints.MinHeight := OptionsGroup.Top + OptionsGroup.Height + CloseBtn.Height + 2*CloseBtn.BorderSpacing.Bottom; - - FAutoSized := true; -end; - -procedure TWeightedKappaFrm.FormCreate(Sender: TObject); -begin - Assert(OS3MainFrm <> nil); - if DictionaryFrm = nil then - Application.CreateForm(TDictionaryFrm, DictionaryFrm); -end; - -procedure TWeightedKappaFrm.FormShow(Sender: TObject); -begin - ResetBtnClick(self); -end; - -procedure TWeightedKappaFrm.HelpBtnClick(Sender: TObject); -begin - if ContextHelpForm = nil then - Application.CreateForm(TContextHelpForm, ContextHelpForm); - ContextHelpForm.HelpMessage((Sender as TButton).tag); -end; - -procedure TWeightedKappaFrm.InputGroupClick(Sender: TObject); -begin - (* - case InputGroup.ItemIndex of - 0: begin // have to count cases in each row and col. combination - NCasesEdit.Enabled := false; - NCasesLbl.Enabled := false; - DepEdit.Enabled := false; - end; - 1: begin // frequencies available for each row and column combo - DepLbl.Enabled := true; - NCasesLbl.Enabled := false; - NCasesEdit.Enabled := false; - DepEdit.Enabled := true; - end; - 2: begin // only proportions available - get N size - DepLbl.Enabled := true; - NCasesEdit.Enabled := true; - NCasesLbl.Enabled := true; - DepEdit.Visible := Enabled; - end; - end; - *) - UpdateBtnStates; -(* - if (index = 2) then // only proportions available - get N size - begin - DepLbl.Visible := true; - NCasesEdit.Visible := true; - NCasesEdit.SetFocus; - DepIn.Enabled := true; - DepOut.Enabled := false; - DepIn.Visible := true; - DepOut.Visible := true; - DepEdit.Visible := true; - NCasesLbl.Visible := true; - end; - if (index = 1) then // frequencies available for each row and column combo - begin - DepLbl.Visible := true; - NCasesEdit.Visible := false; - DepIn.Enabled := true; - DepOut.Enabled := false; - DepIn.Visible := true; - DepOut.Visible := true; - DepEdit.Visible := true; - NCasesLbl.Visible := false; - end; - if (index = 0) then // have to count cases in each row and col. combination - begin - NCasesEdit.Visible := false; - DepIn.Visible := false; - DepOut.Visible := false; - DepEdit.Visible := false; - DepLbl.Visible := false; - NCasesLbl.Visible := false; - end; - *) -end; - -procedure TWeightedKappaFrm.ColInClick(Sender: TObject); +procedure TWeightedKappaForm.ColInClick(Sender: TObject); var index: integer; begin @@ -229,7 +101,7 @@ begin UpdateBtnStates; end; -procedure TWeightedKappaFrm.ColOutClick(Sender: TObject); +procedure TWeightedKappaForm.ColOutClick(Sender: TObject); begin if RaterBEdit.Text <> '' then begin @@ -239,365 +111,278 @@ begin UpdateBtnStates; end; -procedure TWeightedKappaFrm.ComputeBtnClick(Sender: TObject); +procedure TWeightedKappaForm.Compute; var - i, j, k, RowNo, ColNo, DepNo, MinRow, MaxRow, MinCol, MaxCol : integer; - Row, Col, NoSelected, Ncases, Nrows, Ncols, FObs, df : integer; - RowLabels, ColLabels : StrDyneVec; - ColNoSelected : IntDyneVec; - cellstring : string; - outline : string; - Freq : IntDyneMat; - Prop, Expected, CellChi : DblDyneMat; - PObs, ChiSquare, ProbChi, likelihood, Fval, phi : double; - yates, aresult : boolean; - title : string; - Adjchisqr, Adjprobchi, problikelihood, pearsonr : double; - pobserved, SumX, SumY, VarX, VarY, obsdiag, expdiag, expnondiag : double; - pexpected, MantelHaenszel, MHprob, CoefCont, CramerV, Kappa : double; - Frq : integer; - weights, quadweights : DblDyneMat; - lReport: TStrings; + ColNoSelected: IntDyneVec = nil; + Freq: IntDyneMat = nil; + Prop: DblDyneMat = nil; + Expected: DblDyneMat = nil; + CellChi: DblDyneMat = nil; + weights: DblDyneMat = nil; + quadweights: DblDyneMat = nil; + RowLabels: StrDyneVec = nil; + ColLabels: StrDyneVec = nil; + + i, j, k, RowNo, ColNo, DepNo, MinRow, MaxRow, MinCol, MaxCol: integer; + Row, Col, Ncases, Nrows, Ncols, FObs, df: integer; + PObs, ChiSquare, ProbChi, likelihood, Fval, phi: double; + yates: boolean; + title: string; + Adjchisqr, Adjprobchi, problikelihood, pearsonr: double; + pobserved, SumX, SumY, VarX, VarY, obsdiag, expdiag, expnondiag: double; + pexpected, MantelHaenszel, MHprob, CoefCont, CramerV: Double; + KappaU, KappaL, KappaQ: double; + Frq: integer; + lReport: TStrings; begin - if RaterAEdit.Text = '' then - begin - MessageDlg('Rater A is not specified.', mtError, [mbOK], 0); - exit; - end; + pobserved := 0.0; + pexpected := 0.0; - if RaterBEdit.Text = '' then - begin - MessageDlg('Rater B is not specified.', mtError, [mbOK], 0); - exit; - end; + RowNo := GetVariableIndex(OS3MainFrm.DataGrid, RaterAEdit.Text); + ColNo := GetVariableIndex(OS3MainFrm.DataGrid, RaterBEdit.Text); + DepNo := GetVariableIndex(OS3MainFrm.DataGrid, DepEdit.Text); - if InputGroup.ItemIndex > 0 then - begin - if DepEdit.Text = '' then - begin - MessageDlg('Dependent variable is not specified.', mtError, [mbOK], 0); - exit; - end; - end; + if (not DataProcs.ValidValue(RowNo, 1)) or (not DataProcs.ValidValue(ColNo, 1)) then + exit; + if (InputGroup.ItemIndex = 1) and (not DataProcs.ValidValue(DepNo, 1)) then + exit; + if (InputGroup.ItemIndex = 2) and (not DataProcs.ValidValue(DepNo, 0)) then + exit; - if InputGroup.ItemIndex = 2 then - begin - if NCasesEdit.Text = '' then - begin - NCasesEdit.SetFocus; - MessageDlg('Total number of cases is not specified.', mtError, [mbOK], 0); - exit; - end; - if not TryStrToInt(NCasesEdit.Text, i) then - begin - NCasesEdit.SetFocus; - MessageDlg('Total number of cases is not a valid number.', mtError, [mbOK], 0); - exit; - end; - end; + if (InputGroup.ItemIndex = 0) then + SetLength(ColNoSelected, 2) + else + begin + // for reading proportions or frequencies + SetLength(ColNoSelected, 3); + ColNoSelected[2] := DepNo; + end; + ColNoSelected[0] := RowNo; + ColNoSelected[1] := ColNo; - SetLength(ColNoSelected,NoVariables); - yates := false; - RowNo := 0; - ColNo := 0; - DepNo := 0; - pobserved := 0.0; - pexpected := 0.0; + // get min and max of row and col numbers + MinRow := MaxInt; + MaxRow := 0; + MinCol := MaxInt; + MaxCol := 0; + for i := 1 to NoCases do + begin + if (not GoodRecord(OS3MainFrm.DataGrid, i,ColNoSelected)) then Continue; + Row := round(StrToFloat(OS3MainFrm.DataGrid.Cells[RowNo,i])); + Col := round(StrToFloat(OS3MainFrm.DataGrid.Cells[ColNo,i])); + if (Row > MaxRow) then MaxRow := Row; + if (Row < MinRow) then MinRow := Row; + if (Col > MaxCol) then MaxCol := Col; + if (Col < MinCol) then MinCol := Col; + end; + Nrows := MaxRow - MinRow + 1; + Ncols := MaxCol - MinCol + 1; - for i := 1 to NoVariables do - begin - cellstring := OS3MainFrm.DataGrid.Cells[i,0]; - if (cellstring = RaterAEdit.Text) then RowNo := i; - if (cellstring = RaterBEdit.Text) then ColNo := i; - if (cellstring = DepEdit.Text) then DepNo := i; - end; - (* - if ((InputGroup.ItemIndex > 0) and (DepNo = 0)) then - begin - ShowMessage('ERROR! You must select a dependent variable.'); - ColNoSelected := nil; - exit; - end; - if ((RowNo = 0) or (ColNo = 0)) then // || (DepNo == 0)) - begin - ShowMessage('ERROR! A required variable has not been selected.'); - ColNoSelected := nil; - exit; - end; - *) + // allocate and initialize + SetLength(Freq, Nrows+1, Ncols+1); + SetLength(Prop, Nrows+1, Ncols+1); + SetLength(Expected, Nrows, Ncols); + SetLength(CellChi, Nrows, Ncols); + SetLength(RowLabels, Nrows+1); + SetLength(ColLabels, Ncols+1); + for i := 1 to Nrows + 1 do + for j := 1 to Ncols + 1 do Freq[i-1,j-1] := 0; + for i := 1 to Nrows do + RowLabels[i-1] := Format('Row %d', [i]); + RowLabels[Nrows] := 'Total'; + for j := 1 to Ncols do + ColLabels[j-1] := Format('COL. %d', [j]); + ColLabels[Ncols] := 'Total'; - aresult := ValidValue(RowNo,1); - if not aresult then - begin - ColNoSelected := nil; - exit; - end; - aresult := ValidValue(ColNo,1); - if not aresult then - begin - ColNoSelected := nil; - exit; - end; - - ColNoSelected[0] := RowNo; - ColNoSelected[1] := ColNo; - NoSelected := 2; - if (InputGroup.ItemIndex > 0) then // for reading proportions or frequencies - begin - NoSelected := 3; - ColNoSelected[2] := DepNo; - end; - if (InputGroup.ItemIndex = 1) then - begin - aresult := ValidValue(DepNo,1); - if (aresult = false) then - begin - ColNoSelected := nil; - exit; - end; - end; - if (InputGroup.ItemIndex = 2) then - begin - aresult := ValidValue(DepNo,0); - if (aresult = false) then - begin - ColNoSelected := nil; - exit; - end; - end; - - // get min and max of row and col numbers - MinRow := 1000; - MaxRow := 0; - MinCol := 1000; - MaxCol := 0; - for i := 1 to NoCases do - begin - if (not GoodRecord(i,NoSelected,ColNoSelected)) then continue; - Row := round(StrToFloat(OS3MainFrm.DataGrid.Cells[RowNo,i])); - Col := round(StrToFloat(OS3MainFrm.DataGrid.Cells[ColNo,i])); - if (Row > MaxRow) then MaxRow := Row; - if (Row < MinRow) then MinRow := Row; - if (Col > MaxCol) then MaxCol := Col; - if (Col < MinCol) then MinCol := Col; - end; - Nrows := MaxRow - MinRow + 1; - Ncols := MaxCol - MinCol + 1; - - // allocate and initialize - SetLength(Freq,Nrows+1,Ncols+1); - SetLength(Prop,Nrows+1,Ncols+1); - SetLength(Expected,Nrows,Ncols); - SetLength(CellChi,Nrows,Ncols); - SetLength(RowLabels,Nrows+1); - SetLength(ColLabels,Ncols+1); - for i := 1 to Nrows + 1 do - for j := 1 to Ncols + 1 do Freq[i-1,j-1] := 0; - for i := 1 to Nrows do - RowLabels[i-1] := Format('Row %d', [i]); - RowLabels[Nrows] := 'Total'; - for j := 1 to Ncols do - ColLabels[j-1] := Format('COL. %d', [j]); - ColLabels[Ncols] := 'Total'; - - // get cell data - Ncases := 0; - if (InputGroup.ItemIndex = 0) then - begin // count number of cases in each row and column combination - for i := 1 to NoCases do - begin - if (not GoodRecord(i,NoSelected,ColNoSelected)) then - continue; - Ncases := Ncases + 1; - Row := round(StrToFloat(OS3MainFrm.DataGrid.Cells[RowNo,i])); - Col := round(StrToFloat(OS3MainFrm.DataGrid.Cells[ColNo,i])); - Row := Row - MinRow + 1; - Col := Col - MinCol + 1; - Freq[Row-1,Col-1] := Freq[Row-1,Col-1] + 1; - end; - end; - if (InputGroup.ItemIndex = 1) then // read frequencies data from grid - begin - for i := 1 to NoCases do - begin - if (not GoodRecord(i,NoSelected,ColNoSelected)) then continue; - Row := round(StrToFloat(OS3MainFrm.DataGrid.Cells[RowNo,i])); - Col := round(StrToFloat(OS3MainFrm.DataGrid.Cells[ColNo,i])); - Row := Row - MinRow + 1; - Col := Col - MinCol + 1; - FObs := round(StrToFloat(OS3MainFrm.DataGrid.Cells[DepNo,i])); - Freq[Row-1,Col-1] := Freq[Row-1,Col-1] + FObs; - Ncases := Ncases + FObs; - end; - end; - if (InputGroup.ItemIndex = 2) then // get no. of cases and proportions for each cell - begin - Ncases := StrToInt(NCasesEdit.Text); - for i := 1 to NoCases do - begin - if (not GoodRecord(i,NoSelected,ColNoSelected)) then continue; - Row := round(StrToFloat(OS3MainFrm.DataGrid.Cells[RowNo,i])); - Col := round(StrToFloat(OS3MainFrm.DataGrid.Cells[ColNo,i])); - Row := Row - MinRow + 1; - Col := Col - MinCol + 1; - PObs := round(StrToFloat(OS3MainFrm.DataGrid.Cells[DepNo,i])); - Frq := round(PObs * Ncases); - Fval := PObs * Ncases; - if (Fval - Frq < 0.5) then Frq := round(Fval) - else Frq := ceil(Fval); - Freq[Row-1,Col-1] := Freq[Row-1,Col-1] + Frq; - end; - end; - Freq[Nrows,Ncols] := Ncases; - - // Now, calculate expected values - // Get row totals first - for i := 1 to Nrows do - begin - for j := 1 to Ncols do - begin - Freq[i-1,Ncols] := Freq[i-1,Ncols] + Freq[i-1,j-1]; - end; - end; - - // Get col totals next - for j := 1 to Ncols do - begin - for i := 1 to Nrows do + // get cell data + Ncases := 0; + case InputGroup.ItemIndex of + 0: begin // count number of cases in each row and column combination + for i := 1 to NoCases do begin - Freq[Nrows,j-1] := Freq[Nrows,j-1] + Freq[i-1,j-1]; + if (not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected)) then + continue; + Ncases := Ncases + 1; + Row := round(StrToFloat(OS3MainFrm.DataGrid.Cells[RowNo,i])); + Col := round(StrToFloat(OS3MainFrm.DataGrid.Cells[ColNo,i])); + Row := Row - MinRow + 1; + Col := Col - MinCol + 1; + Freq[Row-1,Col-1] := Freq[Row-1,Col-1] + 1; end; - end; + end; + 1: begin // read frequencies data from grid + for i := 1 to NoCases do + begin + if (not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected)) then + continue; + Row := round(StrToFloat(OS3MainFrm.DataGrid.Cells[RowNo,i])); + Col := round(StrToFloat(OS3MainFrm.DataGrid.Cells[ColNo,i])); + Row := Row - MinRow + 1; + Col := Col - MinCol + 1; + FObs := round(StrToFloat(OS3MainFrm.DataGrid.Cells[DepNo,i])); + Freq[Row-1,Col-1] := Freq[Row-1,Col-1] + FObs; + Ncases := Ncases + FObs; + end; + end; + 2: begin // get no. of cases and proportions for each cell + Ncases := StrToInt(NCasesEdit.Text); + for i := 1 to NoCases do + begin + if (not GoodRecord(OS3MainFrm.DataGrid, i, ColNoSelected)) then + continue; + Row := round(StrToFloat(OS3MainFrm.DataGrid.Cells[RowNo,i])); + Col := round(StrToFloat(OS3MainFrm.DataGrid.Cells[ColNo,i])); + Row := Row - MinRow + 1; + Col := Col - MinCol + 1; + PObs := round(StrToFloat(OS3MainFrm.DataGrid.Cells[DepNo,i])); + Frq := round(PObs * Ncases); + Fval := PObs * Ncases; + if (Fval - Frq < 0.5) then + Frq := round(Fval) + else + Frq := ceil(Fval); + Freq[Row-1,Col-1] := Freq[Row-1,Col-1] + Frq; + end; + end; + end; + Freq[Nrows, Ncols] := Ncases; - // Then get expected values and cell chi-squares - ChiSquare := 0.0; - Adjchisqr := 0.0; - if ((YatesChk.Checked) and (Nrows = 2) and (Ncols = 2)) then yates := true; - for i := 1 to Nrows do - begin - for j := 1 to Ncols do - begin - Expected[i-1,j-1] := Freq[Nrows,j-1] * Freq[i-1,Ncols] / Ncases; - if (Expected[i-1,j-1] > 0.0) then - CellChi[i-1,j-1] := sqr(Freq[i-1,j-1] - Expected[i-1,j-1]) / Expected[i-1,j-1] - else - begin - MessageDlg('Zero expected value found.', mtError, [mbOK], 0); - CellChi[i-1,j-1] := 0.0; - end; - ChiSquare := ChiSquare + CellChi[i-1,j-1]; - end; - end; - df := (Nrows - 1) * (Ncols - 1); - if (yates = true) then // 2 x 2 corrected chi-square - begin - Adjchisqr := abs((Freq[0,0] * Freq[1,1]) - (Freq[0,1] * Freq[1,0])); - Adjchisqr := sqr(Adjchisqr - Ncases / 2.0) * Ncases; // numerator - Adjchisqr := Adjchisqr / (Freq[0,2] * Freq[1,2] * Freq[2,0] * Freq[2,1]); - Adjprobchi := 1.0 - chisquaredprob(Adjchisqr,df); - end; - ProbChi := 1.0 - chisquaredprob(ChiSquare,df); // prob. larger chi + // Now, calculate expected values + // Get row totals first + for i := 1 to Nrows do + for j := 1 to Ncols do + Freq[i-1,Ncols] := Freq[i-1,Ncols] + Freq[i-1,j-1]; - //Print results to output form - lReport := TStringList.Create; - try - lReport.Add('CHI-SQUARE ANALYSIS RESULTS FOR ' + RaterAEdit.Text + ' AND ' + RaterBEdit.Text); - lReport.Add('No. of Cases = %d', [Ncases]); + // Get col totals next + for j := 1 to Ncols do + for i := 1 to Nrows do + Freq[Nrows,j-1] := Freq[Nrows,j-1] + Freq[i-1,j-1]; + + // Then get expected values and cell chi-squares + yates := YatesChk.Checked and (Nrows = 2) and (Ncols = 2); + ChiSquare := 0.0; + Adjchisqr := 0.0; + for i := 1 to Nrows do + begin + for j := 1 to Ncols do + begin + Expected[i-1,j-1] := Freq[Nrows,j-1] * Freq[i-1,Ncols] / Ncases; + if (Expected[i-1,j-1] > 0.0) then + CellChi[i-1,j-1] := sqr(Freq[i-1,j-1] - Expected[i-1,j-1]) / Expected[i-1,j-1] + else + begin + ErrorMsg('Zero expected value found.'); + CellChi[i-1,j-1] := 0.0; + end; + ChiSquare := ChiSquare + CellChi[i-1,j-1]; + end; + end; + df := (Nrows - 1) * (Ncols - 1); + if yates then // 2 x 2 corrected chi-square + begin + Adjchisqr := abs((Freq[0,0] * Freq[1,1]) - (Freq[0,1] * Freq[1,0])); + Adjchisqr := sqr(Adjchisqr - Ncases / 2.0) * Ncases; // numerator + Adjchisqr := Adjchisqr / (Freq[0,2] * Freq[1,2] * Freq[2,0] * Freq[2,1]); + Adjprobchi := 1.0 - chisquaredprob(Adjchisqr,df); + end; + ProbChi := 1.0 - chisquaredprob(ChiSquare,df); // prob. larger chi + + //Print results to output form + lReport := TStringList.Create; + try + lReport.Add('CHI-SQUARE ANALYSIS RESULTS FOR ' + RaterAEdit.Text + ' AND ' + RaterBEdit.Text); + lReport.Add('No. of Cases: %d', [Ncases]); + lReport.Add(''); + + // print tables requested by use + if ObsChk.Checked then + begin + IntArrayPrint(Freq, Nrows+1, Ncols+1, 'Frequencies', + RowLabels, ColLabels, 'OBSERVED FREQUENCIES', lReport); + lReport.Add(DIVIDER_SMALL_AUTO); + lReport.Add(''); + end; + + if ExpChk.Checked then + begin + // Expected frequencies + MatPrint(Expected, Nrows, Ncols, 'EXPECTED FREQUENCIES', RowLabels, ColLabels, NoCases, lReport); + lReport.Add(DIVIDER_SMALL_AUTO); + lReport.Add(''); + end; + + if PropChk.Checked then + begin + // Row proportions + for i := 1 to Nrows + 1 do + begin + for j := 1 to Ncols do + begin + if (Freq[i-1, Ncols] > 0.0) then + Prop[i-1,j-1] := Freq[i-1,j-1] / Freq[i-1,Ncols] + else + Prop[i-1,j-1] := 0.0; + end; + if (Freq[i-1, Ncols] > 0.0) then + Prop[i-1, Ncols] := 1.0 + else + Prop[i-1, Ncols] := 0.0; + end; + MatPrint(Prop, Nrows+1, Ncols+1, 'ROW PROPORTIONS', RowLabels, ColLabels, NoCases, lReport); + lReport.Add(DIVIDER_SMALL_AUTO); lReport.Add(''); - // print tables requested by use - if (ObsChk.Checked) then + // Column proportions + for j := 1 to Ncols + 1 do begin - IntArrayPrint(Freq, Nrows+1, Ncols+1, 'Frequencies', - RowLabels, ColLabels, 'OBSERVED FREQUENCIES', lReport); - lReport.Add('------------------------------------------------------------------------------'); - lReport.Add(''); + for i := 1 to Nrows do + begin + if (Freq[Nrows, j-1] > 0.0) then + Prop[i-1, j-1] := Freq[i-1, j-1] / Freq[Nrows, j-1] + else + Prop[i-1, j-1] := 0.0; + end; + if (Freq[Nrows, j-1] > 0.0) then + Prop[Nrows, j-1] := 1.0 + else + Prop[Nrows, j-1] := 0.0; end; + MatPrint(Prop, Nrows+1, Ncols+1, 'COLUMN PROPORTIONS', RowLabels, ColLabels, NoCases, lReport); + lReport.Add(DIVIDER_SMALL_AUTO); + lReport.Add(''); - if (ExpChk.Checked) then - begin - outline := 'EXPECTED FREQUENCIES'; - MatPrint(Expected, Nrows, Ncols, outline, RowLabels, ColLabels, NoCases, lReport); - lReport.Add('------------------------------------------------------------------------------'); - lReport.Add(''); - end; - - if (PropChk.Checked) then - begin - outline := 'ROW PROPORTIONS'; - for i := 1 to Nrows + 1 do - begin - for j := 1 to Ncols do - begin - if (Freq[i-1,Ncols] > 0.0) then - Prop[i-1,j-1] := Freq[i-1,j-1] / Freq[i-1,Ncols] - else - Prop[i-1,j-1] := 0.0; - end; - if (Freq[i-1,Ncols] > 0.0) then - Prop[i-1,Ncols] := 1.0 - else - Prop[i-1][Ncols] := 0.0; - end; - MatPrint(Prop, Nrows+1, Ncols+1, outline, RowLabels, ColLabels, NoCases, lReport); - lReport.Add('------------------------------------------------------------------------------'); - lReport.Add(''); - - outline := 'COLUMN PROPORTIONS'; - for j := 1 to Ncols + 1 do - begin - for i := 1 to Nrows do - begin - if (Freq[Nrows,j-1] > 0.0) then - Prop[i-1,j-1] := Freq[i-1,j-1] / Freq[Nrows,j-1] - else - Prop[i-1,j-1] := 0.0; - end; - if (Freq[Nrows,j-1] > 0.0) then - Prop[Nrows][j-1] := 1.0 - else - Prop[Nrows,j-1] := 0.0; - end; - MatPrint(Prop, Nrows+1, Ncols+1, outline, RowLabels, ColLabels, NoCases, lReport); - lReport.Add('------------------------------------------------------------------------------'); - lReport.Add(''); - - outline := 'PROPORTIONS OF TOTAL N'; - for i := 1 to Nrows + 1 do - for j := 1 to Ncols + 1 do Prop[i-1,j-1] := Freq[i-1,j-1] / Ncases; - Prop[Nrows,Ncols] := 1.0; - MatPrint(Prop, Nrows+1, Ncols+1, outline, RowLabels, ColLabels, NoCases, lReport); - lReport.Add('------------------------------------------------------------------------------'); - lReport.Add(''); + // Proportions of total n + for i := 1 to Nrows + 1 do + for j := 1 to Ncols + 1 do Prop[i-1,j-1] := Freq[i-1,j-1] / Ncases; + Prop[Nrows,Ncols] := 1.0; + MatPrint(Prop, Nrows+1, Ncols+1, 'PROPORTIONS OF TOTAL N', RowLabels, ColLabels, NoCases, lReport); + lReport.Add(DIVIDER_SMALL_AUTO); + lReport.Add(''); end; - if (ChiChk.Checked) then + if ChiChk.Checked then begin - outline := 'CHI-SQUARED VALUE FOR CELLS'; - MatPrint(CellChi, Nrows, Ncols, outline, RowLabels, ColLabels, NoCases, lReport); - lReport.Add('------------------------------------------------------------------------------'); - lReport.Add(''); + // Chi-squared value for cells + MatPrint(CellChi, Nrows, Ncols, 'CHI-SQUARED VALUE FOR CELLS', RowLabels, ColLabels, NoCases, lReport); + lReport.Add(DIVIDER_SMALL_AUTO); + lReport.Add(''); end; - lReport.Add(''); lReport.Add('Chi-square: %.3f with D.F. %d. Prob. > value %.3f', [ChiSquare, df, ProbChi]); - lReport.Add(''); if yates then - lReport.Add('Chi-square using Yates correction %.3f and Prob > value %.3f', [Adjchisqr, Adjprobchi]); + lReport.Add('Chi-square using Yates correction %.3f and Prob > value %.3f', [Adjchisqr, Adjprobchi]); likelihood := 0.0; for i := 0 to Nrows - 1 do - for j := 0 to Ncols - 1 do - likelihood := likelihood + Freq[i,j] * (ln(Expected[i,j] / Freq[i,j])); + for j := 0 to Ncols - 1 do + likelihood := likelihood + Freq[i,j] * (ln(Expected[i,j] / Freq[i,j])); likelihood := -2.0 * likelihood; problikelihood := 1.0 - chisquaredprob(likelihood,df); lReport.Add('Likelihood Ratio %.3f with prob. > value %.4f', [likelihood, problikelihood]); - lReport.Add(''); phi := sqrt(ChiSquare / Ncases); lReport.Add('phi correlation: %.4f', [phi]); - lReport.Add(''); pearsonr := 0.0; SumX := 0.0; @@ -611,21 +396,18 @@ begin VarX := VarX - ((SumX * SumX) / Ncases); VarY := VarY - ((SumY * SumY) / Ncases); for i := 0 to Nrows - 1 do - for j := 0 to Ncols - 1 do - pearsonr := pearsonr + ((i+1)*(j+1) * Freq[i,j]); + for j := 0 to Ncols - 1 do + pearsonr := pearsonr + ((i+1)*(j+1) * Freq[i,j]); pearsonr := pearsonr - (SumX * SumY / Ncases); pearsonr := pearsonr / sqrt(VarX * VarY); - lReport.Add('Pearson Correlation r = %6.4f', [pearsonr]); - lReport.Add(''); + lReport.Add('Pearson Correlation r: %.4f', [pearsonr]); MantelHaenszel := (Ncases-1) * (pearsonr * pearsonr); MHprob := 1.0 - chisquaredprob(MantelHaenszel,1); lReport.Add('Mantel-Haenszel Test of Linear Association: %.3f with probability > value = %.4f', [MantelHaenszel, MHprob]); - lReport.Add(''); CoefCont := sqrt(ChiSquare / (ChiSquare + Ncases)); - lReport.Add('The coefficient of contingency: %.3f', [CoefCont]); - lReport.Add(''); + lReport.Add('Coefficient of contingency: %.3f', [CoefCont]); if (Nrows < Ncols) then CramerV := sqrt(ChiSquare / (Ncases * ((Nrows-1)))) @@ -636,129 +418,132 @@ begin // kappa if (Nrows = Ncols) then begin - obsdiag := 0.0; - expdiag := 0.0; - for i := 0 to Nrows - 1 do - begin - obsdiag := obsdiag + Freq[i,i]; - expdiag := expdiag + Expected[i,i]; - end; - expnondiag := Ncases - expdiag; - Kappa := (obsdiag - expdiag) / expnondiag; - lReport.Add(''); - lReport.Add('Unweighted Kappa: %.4f', [Kappa]); + lReport.Add(''); + lReport.Add(DIVIDER_AUTO); + lReport.Add(''); - // get linear weights - SetLength(weights,Nrows,Ncols); - SetLength(quadweights,Nrows,Ncols); - for i := 0 to Nrows - 1 do - begin - for j := 0 to Ncols - 1 do - begin - weights[i,j] := 0.0; - quadweights[i,j] := 0.0; - end; - end; - for i := 0 to Nrows - 1 do - begin - for j := 0 to Ncols - 1 do - begin - weights[i,j] := 1.0 - (abs((i-j)) / (Nrows-1)); - quadweights[i,j] := 1.0 - ( abs((i-j)*(i-j)) / ((Nrows-1)*(Nrows-1)) ); - end; - end; + obsdiag := 0.0; + expdiag := 0.0; + for i := 0 to Nrows - 1 do + begin + obsdiag := obsdiag + Freq[i,i]; + expdiag := expdiag + Expected[i,i]; + end; + expnondiag := Ncases - expdiag; + // Unweighted kappa + KappaU := (obsdiag - expdiag) / expnondiag; - outline := 'Observed Linear Weights'; - MatPrint(weights, Nrows, Ncols, outline, RowLabels, ColLabels, NoCases, lReport); - lReport.Add('------------------------------------------------------------------------------'); - lReport.Add(''); - - outline := 'Observed Quadratic Weights'; - MatPrint(quadweights, Nrows, Ncols, outline, RowLabels, ColLabels, NoCases, lReport); - lReport.Add('------------------------------------------------------------------------------'); - lReport.Add(''); - - for i := 0 to Nrows - 1 do - begin - for j := 0 to Ncols - 1 do - begin - pobserved := pobserved + (Freq[i][j] / Ncases) * weights[i,j]; - pexpected := pexpected + (Expected[i,j] / Ncases) * weights[i,j]; - end; - end; - Kappa := (pobserved - pexpected) / (1.0 - pexpected); - lReport.Add('Linear Weighted Kappa: %.4f', [Kappa]); - - pobserved := 0.0; - pexpected := 0.0; - for i := 0 to Nrows - 1 do - begin - for j := 0 to Ncols - 1 do - begin - pobserved := pobserved + (Freq[i,j] / Ncases) * quadweights[i,j]; - pexpected := pexpected + (Expected[i,j] / Ncases) * quadweights[i,j]; - end; - end; - Kappa := (pobserved - pexpected) / (1.0 - pexpected); - lReport.Add('Quadratic Weighted Kappa: %.4f', [Kappa]); - quadweights := nil; - weights := nil; + // get linear weights + SetLength(weights,Nrows,Ncols); + SetLength(quadweights,Nrows,Ncols); + for i := 0 to Nrows - 1 do + begin + for j := 0 to Ncols - 1 do + begin + weights[i,j] := 0.0; + quadweights[i,j] := 0.0; + end; + end; + for i := 0 to Nrows - 1 do + begin + for j := 0 to Ncols - 1 do + begin + weights[i,j] := 1.0 - (abs((i-j)) / (Nrows-1)); + quadweights[i,j] := 1.0 - ( abs((i-j)*(i-j)) / ((Nrows-1)*(Nrows-1)) ); + end; end; - DisplayReport(lReport); - finally - lReport.Free; + // Observed Linear Weights + MatPrint(weights, Nrows, Ncols, 'Observed Linear Weights', RowLabels, ColLabels, NoCases, lReport); + lReport.Add(DIVIDER_SMALL_AUTO); + lReport.Add(''); + + // Observed Quadratic Weights + MatPrint(quadweights, Nrows, Ncols, 'Observed Quadratic Weights', RowLabels, ColLabels, NoCases, lReport); + lReport.Add(DIVIDER_SMALL_AUTO); + lReport.Add(''); + + for i := 0 to Nrows - 1 do + begin + for j := 0 to Ncols - 1 do + begin + pobserved := pobserved + (Freq[i][j] / Ncases) * weights[i,j]; + pexpected := pexpected + (Expected[i,j] / Ncases) * weights[i,j]; + end; + end; + // Linear weighted kappa + KappaL := (pobserved - pexpected) / (1.0 - pexpected); + + pobserved := 0.0; + pexpected := 0.0; + for i := 0 to Nrows - 1 do + begin + for j := 0 to Ncols - 1 do + begin + pobserved := pobserved + (Freq[i,j] / Ncases) * quadweights[i,j]; + pexpected := pexpected + (Expected[i,j] / Ncases) * quadweights[i,j]; + end; + end; + // Quadratic weighted kappa + KappaQ := (pobserved - pexpected) / (1.0 - pexpected); + + lReport.Add('Unweighted Kappa: %.4f', [KappaU]); + lReport.Add('Linear Weighted Kappa: %.4f', [KappaL]); + lReport.Add('Quadratic Weighted Kappa: %.4f', [KappaQ]); end; - // save frequency data file if elected - if ((SaveChk.Checked) and (InputGroup.ItemIndex = 0)) then + FReportFrame.DisplayReport(lReport); + finally + lReport.Free; + end; + + // save frequency data file if elected + if (SaveChk.Checked and (InputGroup.ItemIndex = 0)) then + begin + ClearGrid; + for i := 1 to 3 do DictionaryFrm.NewVar(i); + DictionaryFrm.DictGrid.Cells[1,1] := 'ROW'; + DictionaryFrm.DictGrid.Cells[1,2] := 'COL'; + DictionaryFrm.DictGrid.Cells[1,3] := 'FREQ.'; + OS3MainFrm.DataGrid.Cells[1,0] := 'ROW'; + OS3MainFrm.DataGrid.Cells[2,0] := 'COL'; + OS3MainFrm.DataGrid.Cells[3,0] := 'Freq.'; + k := 1; + for i := 1 to Nrows do begin - ClearGrid; - for i := 1 to 3 do DictionaryFrm.NewVar(i); - DictionaryFrm.DictGrid.Cells[1,1] := 'ROW'; - DictionaryFrm.DictGrid.Cells[1,2] := 'COL'; - DictionaryFrm.DictGrid.Cells[1,3] := 'FREQ.'; - OS3MainFrm.DataGrid.Cells[1,0] := 'ROW'; - OS3MainFrm.DataGrid.Cells[2,0] := 'COL'; - OS3MainFrm.DataGrid.Cells[3,0] := 'Freq.'; - k := 1; - for i := 1 to Nrows do - begin - for j := 1 to Ncols do - begin - OS3MainFrm.DataGrid.RowCount := k + 1; - OS3MainFrm.DataGrid.Cells[1,k] := IntToStr(i); - OS3MainFrm.DataGrid.Cells[2,k] := IntToStr(j); - OS3MainFrm.DataGrid.Cells[3,k] := IntToStr(Freq[i-1,j-1]); - k := k + 1; - end; - end; - for i := 1 to k - 1 do - begin - title := 'CASE ' + IntToStr(i); - OS3MainFrm.DataGrid.Cells[0,i] := title; - end; - title := InputBox('FILE:','File Name:','Frequencies.LAZ'); - OS3MainFrm.FileNameEdit.Text := title; - OS3MainFrm.NoVarsEdit.Text := IntToStr(3); - OS3MainFrm.NoCasesEdit.Text := IntToStr(k-1); - NoVariables := 3; - NoCases := k-1; - - SaveOS2File; + for j := 1 to Ncols do + begin + OS3MainFrm.DataGrid.RowCount := k + 1; + OS3MainFrm.DataGrid.Cells[1,k] := IntToStr(i); + OS3MainFrm.DataGrid.Cells[2,k] := IntToStr(j); + OS3MainFrm.DataGrid.Cells[3,k] := IntToStr(Freq[i-1,j-1]); + k := k + 1; + end; end; + for i := 1 to k - 1 do + OS3MainFrm.DataGrid.Cells[0,i] := 'CASE ' + IntToStr(i); + title := InputBox('FILE:', 'File Name:', 'Frequencies.laz'); + OS3MainFrm.FileNameEdit.Text := title; + OS3MainFrm.NoVarsEdit.Text := IntToStr(3); + OS3MainFrm.NoCasesEdit.Text := IntToStr(k-1); + NoVariables := 3; + NoCases := k-1; - //clean up - ColLabels := nil; - RowLabels := nil; - CellChi := nil; - Expected := nil; - Prop := nil; - Freq := nil; - ColNoSelected := nil; + SaveOS2File; + end; + + //clean up + ColLabels := nil; + RowLabels := nil; + CellChi := nil; + Expected := nil; + Prop := nil; + Freq := nil; + ColNoSelected := nil; end; -procedure TWeightedKappaFrm.DepInClick(Sender: TObject); + +procedure TWeightedKappaForm.DepInClick(Sender: TObject); var index: integer; begin @@ -771,7 +556,8 @@ begin UpdateBtnStates; end; -procedure TWeightedKappaFrm.DepOutClick(Sender: TObject); + +procedure TWeightedKappaForm.DepOutClick(Sender: TObject); begin if DepEdit.Text <> '' then begin @@ -781,7 +567,59 @@ begin UpdateBtnStates; end; -procedure TWeightedKappaFrm.UpdateBtnStates; + +procedure TWeightedKappaForm.FormCreate(Sender: TObject); +begin + if DictionaryFrm = nil then + Application.CreateForm(TDictionaryFrm, DictionaryFrm); +end; + + +procedure TWeightedKappaForm.InputGroupClick(Sender: TObject); +begin + UpdateBtnStates; +end; + + +procedure TWeightedKappaForm.Reset; +begin + RaterAEdit.Text := ''; + RaterBEdit.Text := ''; + DepEdit.Text := ''; + NCasesEdit.Text := ''; + + CollectVariableNames(OS3MainFrm.DataGrid, VarList.Items); + + UpdateBtnStates; +end; + + +procedure TWeightedKappaForm.RowInClick(Sender: TObject); +var + index: integer; +begin + index := VarList.ItemIndex; + if (index > -1) and (RaterAEdit.Text = '') then + begin + RaterAEdit.Text := VarList.Items[index]; + VarList.Items.Delete(index); + end; + UpdateBtnStates; +end; + + +procedure TWeightedKappaForm.RowOutClick(Sender: TObject); +begin + if RaterAEdit.Text <> '' then + begin + VarList.Items.Add(RaterAEdit.Text); + RaterAEdit.Text := ''; + end; + UpdateBtnStates; +end; + + +procedure TWeightedKappaForm.UpdateBtnStates; begin RowIn.Enabled := (VarList.ItemIndex > -1) and (RaterAEdit.Text = ''); RowOut.Enabled := (RaterAEdit.Text <> ''); @@ -798,13 +636,91 @@ begin NCasesLbl.Enabled := NCasesEdit.Enabled; end; -procedure TWeightedKappaFrm.VarListSelectionChange(Sender: TObject; User: boolean); + +function TWeightedKappaForm.Validate(out AMsg: String; out AControl: TWinControl): Boolean; +var + n: Integer; +begin + Result := false; + + if InputGroup.ItemIndex = -1 then + begin + AMsg := 'No input option selected.'; + AControl := InputGroup; + exit; + end; + + if RaterAEdit.Text = '' then + begin + AMsg := 'Rater A is not specified.'; + AControl := RaterAEdit; + exit; + end; + + if RaterBEdit.Text = '' then + begin + AMsg := 'Rater B is not specified.'; + AControl := RaterBEdit; + exit; + end; + + if InputGroup.ItemIndex > 0 then + begin + if DepEdit.Text = '' then + begin + AMsg := 'Dependent variable is not specified.'; + AControl := DepEdit; + exit; + end; + end; + + if InputGroup.ItemIndex = 2 then + begin + if NCasesEdit.Text = '' then + begin + AMsg := 'Total number of cases is not specified.'; + AControl := NCasesEdit; + exit; + end; + if not TryStrToInt(NCasesEdit.Text, n) then + begin + AMsg := 'Total number of cases is not a valid number.'; + AControl := NCasesEdit; + exit; + end; + end; + + Result := true; +end; + + +procedure TWeightedKappaForm.VarListDblClick(Sender: TObject); +var + index: Integer; +begin + index := VarList.ItemIndex; + if index > -1 then begin + if RaterAEdit.Text = '' then + RaterAEdit.Text := VarList.Items[index] + else + if RaterBEdit.Text = '' then + RaterBEdit.Text := VarList.Items[index] + else + if DepEdit.Enabled and (DepEdit.Text = '') then + DepEdit.Text := VarList.Items[index] + else + exit; + VarList.Items.Delete(index); + UpdateBtnStates; + end; +end; + + +procedure TWeightedKappaForm.VarListSelectionChange(Sender: TObject; User: boolean); begin UpdateBtnStates; end; -initialization - {$I kappaunit.lrs} end. diff --git a/applications/lazstats/source/forms/mainunit.pas b/applications/lazstats/source/forms/mainunit.pas index b80995a76..12ba89826 100644 --- a/applications/lazstats/source/forms/mainunit.pas +++ b/applications/lazstats/source/forms/mainunit.pas @@ -1777,9 +1777,9 @@ end; // Menu "Analysis" > "Nonparametric" > "Kappa and Weighted Kappa" procedure TOS3MainFrm.mnuAnalysisNonPar_WeightedKappaClick(Sender: TObject); begin - if WeightedKappaFrm = nil then - Application.CreateForm(TWeightedKappaFrm, WeightedKappaFrm); - WeightedKappaFrm.ShowModal; + if WeightedKappaForm = nil then + Application.CreateForm(TWeightedKappaForm, WeightedKappaForm); + WeightedKappaForm.ShowModal; end; // Menu "Analysis" > "Nonparametric" > "Life table"