Skip to content

Commit

Permalink
Add t-test and make textual summary dynamic
Browse files Browse the repository at this point in the history
  • Loading branch information
yaramt committed Aug 5, 2024
1 parent f756e51 commit 92a7e39
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 29 deletions.
52 changes: 40 additions & 12 deletions app/lib/screens/study/report/sections/average_section_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,38 @@ import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:studyu_app/screens/study/report/report_section_widget.dart';
import 'package:studyu_app/screens/study/report/sections/results_descriptive_statistics.dart';
import 'package:studyu_app/screens/study/report/sections/results_gauge.dart';
import 'package:studyu_app/screens/study/report/sections/results_textual_summary.dart';
import 'package:studyu_app/screens/study/report/util/plot_utilities.dart';
import 'package:studyu_app/theme.dart';
import 'package:studyu_app/util/data_processing.dart';
import 'package:studyu_core/core.dart';
import 'results_descriptive_statistics.dart';
import 'results_gauge.dart';
import 'results_textual_summary.dart';

class AverageSectionWidget extends ReportSectionWidget {
final AverageSection section;
//test

const AverageSectionWidget(super.subject, this.section, {super.key});

@override
Widget build(BuildContext context) {
ValueNotifier<bool> showDescriptionNotifier = ValueNotifier(false);

final data = getAggregatedData().toList();
final aggregatedDataByDay =
aggregateDataBy(null).toList();
// Filter out baseline data
final filteredData = aggregatedDataByDay.where((datum) => datum.intervention != '__baseline');
// Group data by intervention
final interventionGroups = filteredData.fold<Map<String, List<num>>>({}, (map, datum) {
map.putIfAbsent(datum.intervention, () => []).add(datum.value);
return map;
});
// Extract keys from the map
final keys = interventionGroups.keys.toList();
// Define default empty lists
final List<num> valuesInterventionA = keys.isNotEmpty ? interventionGroups[keys[0]]! : [];
final List<num> valuesInterventionB = keys.length > 1 ? interventionGroups[keys[1]]! : [];
final String nameInterventionA = keys.isNotEmpty ? getInterventionNameFromInterventionId(context, keys[0])! : "";
final String nameInterventionB = keys.length > 1 ? getInterventionNameFromInterventionId(context, keys[1])! : "";
final taskTitle = subject.study.observations
.firstWhereOrNull(
(element) => element.id == section.resultProperty!.task,
Expand All @@ -37,15 +51,15 @@ class AverageSectionWidget extends ReportSectionWidget {
.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
const TextualSummaryWidget(), //Row 2
TextualSummaryWidget(valuesInterventionA, valuesInterventionB, nameInterventionA, nameInterventionB, subject, section), //Row 2
const SizedBox(height: 8),
const GaugeTitleWidget(), //Row 3
const SizedBox(height: 8),
const GaugesWidget(), //Row 4
const SizedBox(height: 8),
getLegend(context, data),
const SizedBox(height: 8),
ExpansionTile(
const ExpansionTile(
title: Text('Descriptive Statistics'),
children: [
DescriptiveStatisticsWidget(),
Expand Down Expand Up @@ -267,7 +281,7 @@ class AverageSectionWidget extends ReportSectionWidget {
return subject.getDayOfStudyFor(key) - offset;
}

Iterable<DiagramDatum> getAggregatedData() {
Iterable<DiagramDatum> aggregateDataBy(TemporalAggregation? aggregate) {
final values = section.resultProperty!.retrieveFromResults(subject);
final data = values.entries.map(
(e) => DiagramDatum(
Expand All @@ -277,8 +291,9 @@ class AverageSectionWidget extends ReportSectionWidget {
subject.getInterventionForDate(e.key)!.id,
),
);

if (section.aggregate == TemporalAggregation.day) {
if (aggregate == null) {
return data;
} else if (aggregate == TemporalAggregation.day) {
return data
.groupBy((e) => e.x)
.aggregateWithKey(
Expand All @@ -290,7 +305,7 @@ class AverageSectionWidget extends ReportSectionWidget {
),
)
.map((e) => e.value);
} else if (section.aggregate == TemporalAggregation.phase) {
} else if (aggregate == TemporalAggregation.phase) {
return data
.groupBy((e) => subject.getInterventionIndexForDate(e.timestamp!))
.aggregateWithKey(
Expand Down Expand Up @@ -318,6 +333,10 @@ class AverageSectionWidget extends ReportSectionWidget {
}
}

Iterable<DiagramDatum> getAggregatedData() {
return aggregateDataBy(section.aggregate);
}

Map<String, String?> getInterventionNames(BuildContext context) {
final names = {
for (final intervention in subject.study.interventions)
Expand All @@ -326,6 +345,15 @@ class AverageSectionWidget extends ReportSectionWidget {
names[Study.baselineID] = AppLocalizations.of(context)!.baseline;
return names;
}

String? getInterventionNameFromInterventionId(BuildContext context, String interventionId) {
for (final intervention in subject.study.interventions) {
if (intervention.id == interventionId) {
return intervention.name;
}
}
return null;
}
}

class DiagramDatum {
Expand Down
78 changes: 61 additions & 17 deletions app/lib/screens/study/report/sections/results_textual_summary.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
import 'package:flutter/material.dart';
// Row 2: Textual Summary
import 'package:statistics/statistics.dart';
import 'package:studyu_app/screens/study/report/sections/average_section_widget.dart';
import 'package:t_stats/t_stats.dart';

class TextualSummaryWidget extends StatelessWidget {
const TextualSummaryWidget({super.key});
class TextualSummaryWidget extends AverageSectionWidget {
final List<num> valuesInterventionA;
final List<num> valuesInterventionB;
final String nameInterventionA;
final String nameInterventionB;

const TextualSummaryWidget(
this.valuesInterventionA,
this.valuesInterventionB,
this.nameInterventionA,
this.nameInterventionB,
super.subject,
super.section,
{super.key,});

@override
Widget build(BuildContext context) {
// Two Sample t-test
// TODO: t-test library license issues
final stats = Statistic.from(valuesInterventionA);
final stats2 = Statistic.from(valuesInterventionB);
// Determine the summary text based on t-test results
final summaryText = getTextualSummary(stats.isDifferentFrom(stats2));
return Row(
children: <Widget>[
Expanded(
Expand All @@ -18,20 +38,20 @@ class TextualSummaryWidget extends StatelessWidget {
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(12.0),
),
child: const Column(
child: Column(
children: <Widget>[
Text(
'With tea',
style: TextStyle(
nameInterventionA,
style: const TextStyle(
fontSize: 16,
color: Colors.green,
color: Colors.blue,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4), // SizedBox for spacing
const SizedBox(height: 4), // SizedBox for spacing
Text(
'Your sleep quality was slightly better', // Text 1 for box 1
style: TextStyle(
summaryText[0],
style: const TextStyle(
fontSize: 12,
color: Colors.black,
),
Expand All @@ -52,20 +72,20 @@ class TextualSummaryWidget extends StatelessWidget {
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(12.0),
),
child: const Column(
child: Column(
children: <Widget>[
Text(
'Without tea',
style: TextStyle(
nameInterventionB,
style: const TextStyle(
fontSize: 16,
color: Colors.red,
color: Colors.orange,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4), // SizedBox for spacing
const SizedBox(height: 4),
Text(
'Your sleep quality was slightly worse', // Text 2 for box 2
style: TextStyle(
summaryText[1],
style: const TextStyle(
fontSize: 12,
color: Colors.black,
),
Expand All @@ -79,4 +99,28 @@ class TextualSummaryWidget extends StatelessWidget {
],
);
}

// This method returns textual outcomes of a two sample t-test with respect to each of the interventions A and B.
List<String> getTextualSummary(bool isDifferent) {
List<String> textualSummaryInterventionAB;
if (isDifferent) {
if (valuesInterventionA.mean > valuesInterventionB.mean) {
textualSummaryInterventionAB = [
"Your ${section.title} was better during intervention: $nameInterventionA compared to: $nameInterventionB.",
"Your ${section.title} was worse in intervention: $nameInterventionB compared to: $nameInterventionA.",
];
} else {
textualSummaryInterventionAB = [
"Your ${section.title} was worse during intervention: $nameInterventionA compared to: $nameInterventionB.",
"Your ${section.title} was better during intervention: $nameInterventionB compared to: $nameInterventionA.",
];
}
} else {
textualSummaryInterventionAB = [
"There was no significant difference in ${section.title} between interventions: $nameInterventionA and $nameInterventionB.",
"There was no significant difference in ${section.title} between interventions: $nameInterventionA and $nameInterventionB.",
];
}
return textualSummaryInterventionAB;
}
}
32 changes: 32 additions & 0 deletions app/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.8"
base_codecs:
dependency: transitive
description:
name: base_codecs
sha256: "41701a12ede9912663decd708279924ece5018566daa7d1f484d5f4f10894f91"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
bidi:
dependency: transitive
description:
Expand Down Expand Up @@ -249,6 +257,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.8"
data_serializer:
dependency: transitive
description:
name: data_serializer
sha256: "44f0e3f2b82f54bce125f2f8ed7900e5d5700603c5fa033dcc1d89b0714da3a6"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
dbus:
dependency: transitive
description:
Expand Down Expand Up @@ -1170,6 +1186,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.11.1"
statistics:
dependency: "direct main"
description:
name: statistics
sha256: ff25b9d4b46625ace470cc337a20206e5aa5e67044ff0bfbb0c28b40d8bee2d6
url: "https://pub.dev"
source: hosted
version: "1.1.1"
storage_client:
dependency: transitive
description:
Expand Down Expand Up @@ -1264,6 +1288,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.0+1"
t_stats:
dependency: "direct main"
description:
name: t_stats
sha256: ebe4babf8fba8140d4a66465707a5f3db43cf31c9159df225fee823973d467c6
url: "https://pub.dev"
source: hosted
version: "3.0.0"
term_glyph:
dependency: transitive
description:
Expand Down
2 changes: 2 additions & 0 deletions app/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ dependencies:
flutter_timezone: ^1.0.8
flutter_web_plugins:
sdk: flutter
t_stats: ^3.0.0
flutter_widget_from_html: ^0.15.1
intersperse: ^2.0.0
intl: ^0.19.0
Expand All @@ -39,6 +40,7 @@ dependencies:
sentry_flutter: ^8.2.0
sentry_logging: ^8.2.0
shared_preferences: ^2.2.3
statistics: ^1.1.1
studyu_core: ^4.4.3-dev.1
studyu_flutter_common: ^1.8.4-dev.1
supabase: ^2.2.1
Expand Down

0 comments on commit 92a7e39

Please sign in to comment.