| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- // tools/chi_square.dart
- import 'package:flutter/material.dart';
- import 'package:rational/rational.dart';
- import 'package:fl_chart/fl_chart.dart';
- import '../utils/math_utils.dart';
- class ChiSquarePage extends StatefulWidget {
- @override
- _ChiSquarePageState createState() => _ChiSquarePageState();
- }
- class _ChiSquarePageState extends State<ChiSquarePage> {
- final TextEditingController aController = TextEditingController();
- final TextEditingController bController = TextEditingController();
- final TextEditingController cController = TextEditingController();
- final TextEditingController dController = TextEditingController();
- String result = '';
- List<BarChartGroupData> barGroups = [];
- bool showChart = false;
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(title: Text('卡方检验')),
- body: Padding(
- padding: EdgeInsets.all(16),
- child: Column(
- children: [
- Text('请输入2×2列联表数据:'),
- SizedBox(height: 20),
- Row(
- children: [
- Expanded(child: _buildInputField('a', aController)),
- SizedBox(width: 10),
- Expanded(child: _buildInputField('b', bController)),
- ],
- ),
- Row(
- children: [
- Expanded(child: _buildInputField('c', cController)),
- SizedBox(width: 10),
- Expanded(child: _buildInputField('d', dController)),
- ],
- ),
- SizedBox(height: 20),
- ElevatedButton(
- onPressed: _calculate,
- child: Text('计算卡方值'),
- ),
- SizedBox(height: 20),
- if (result.isNotEmpty)
- Card(
- child: Padding(
- padding: EdgeInsets.all(16),
- child: Text(
- result,
- style: TextStyle(fontSize: 16),
- ),
- ),
- ),
- SizedBox(height: 20),
- if (showChart && barGroups.isNotEmpty)
- Expanded(
- child: Card(
- elevation: 4,
- child: Padding(
- padding: EdgeInsets.all(16),
- child: Column(
- children: [
- Text(
- '等高堆积条形图',
- style: TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.bold,
- ),
- ),
- SizedBox(height: 10),
- Expanded(
- child: BarChart(
- BarChartData(
- alignment: BarChartAlignment.spaceAround,
- maxY: 1.0,
- barTouchData: BarTouchData(
- enabled: false,
- ),
- titlesData: FlTitlesData(
- show: true,
- bottomTitles: AxisTitles(
- sideTitles: SideTitles(
- showTitles: true,
- getTitlesWidget: (value, meta) {
- switch (value.toInt()) {
- case 0:
- return Text('组1');
- case 1:
- return Text('组2');
- default:
- return Text('');
- }
- },
- ),
- ),
- leftTitles: AxisTitles(
- sideTitles: SideTitles(
- showTitles: true,
- getTitlesWidget: (value, meta) {
- return Text('${(value * 100).toInt()}%');
- },
- reservedSize: 40,
- ),
- ),
- topTitles: AxisTitles(
- sideTitles: SideTitles(showTitles: false),
- ),
- rightTitles: AxisTitles(
- sideTitles: SideTitles(showTitles: false),
- ),
- ),
- gridData: FlGridData(
- show: true,
- drawVerticalLine: false,
- getDrawingHorizontalLine: (value) {
- return FlLine(
- color: Colors.grey.withOpacity(0.3),
- strokeWidth: 1,
- );
- },
- ),
- borderData: FlBorderData(
- show: false,
- ),
- barGroups: barGroups,
- ),
- ),
- ),
- SizedBox(height: 10),
- Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- _buildLegend(Colors.blue, '类别A'),
- SizedBox(width: 20),
- _buildLegend(Colors.red, '类别B'),
- ],
- ),
- ],
- ),
- ),
- ),
- ),
- ],
- ),
- ),
- );
- }
- Widget _buildInputField(String label, TextEditingController controller) {
- return TextField(
- controller: controller,
- keyboardType: TextInputType.numberWithOptions(signed: true, decimal: true),
- decoration: InputDecoration(
- labelText: label,
- border: OutlineInputBorder(),
- ),
- );
- }
- Widget _buildLegend(Color color, String text) {
- return Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Container(
- width: 16,
- height: 16,
- color: color,
- ),
- SizedBox(width: 8),
- Text(text),
- ],
- );
- }
- void _calculate() {
- try {
- final a = int.parse(aController.text);
- final b = int.parse(bController.text);
- final c = int.parse(cController.text);
- final d = int.parse(dController.text);
- final n = a + b + c + d;
- final numerator = Rational.fromInt(n) *
- Rational.fromInt((a * d - b * c) * (a * d - b * c));
- final denominator = Rational.fromInt((a + b) * (c + d) * (a + c) * (b + d));
- final K2 = numerator / denominator;
- // 计算比例用于图表
- final totalGroup1 = a + b;
- final totalGroup2 = c + d;
-
- final proportionA1 = totalGroup1 > 0 ? a / totalGroup1 : 0.0;
- final proportionB1 = totalGroup1 > 0 ? b / totalGroup1 : 0.0;
- final proportionA2 = totalGroup2 > 0 ? c / totalGroup2 : 0.0;
- final proportionB2 = totalGroup2 > 0 ? d / totalGroup2 : 0.0;
- // 创建条形图数据 - 修复堆积问题
- barGroups = [
- BarChartGroupData(
- x: 0,
- groupVertically: true, // 启用垂直堆积
- barsSpace: 4, // 条形之间的间距
- barRods: [
- BarChartRodData(
- fromY: 0,
- toY: proportionA1,
- color: Colors.blue,
- width: 30,
- ),
- BarChartRodData(
- fromY: proportionA1, // 从第一个条形的顶部开始
- toY: proportionA1 + proportionB1,
- color: Colors.red,
- width: 30,
- ),
- ],
- ),
- BarChartGroupData(
- x: 1,
- groupVertically: true, // 启用垂直堆积
- barsSpace: 4, // 条形之间的间距
- barRods: [
- BarChartRodData(
- fromY: 0,
- toY: proportionA2,
- color: Colors.blue,
- width: 30,
- ),
- BarChartRodData(
- fromY: proportionA2, // 从第一个条形的顶部开始
- toY: proportionA2 + proportionB2,
- color: Colors.red,
- width: 30,
- ),
- ],
- ),
- ];
- setState(() {
- result = 'K² = ${MathUtils.rationalToString(K2)}\n'
- '近似值: ${K2.toDouble().toStringAsFixed(4)}';
- showChart = true;
- });
- } catch (e) {
- setState(() {
- result = '错误: $e';
- showChart = false;
- });
- }
- }
- @override
- void dispose() {
- aController.dispose();
- bController.dispose();
- cController.dispose();
- dController.dispose();
- super.dispose();
- }
- }
|