/** * 生成分类变量(分箱)对话框 - 改进版 * * 改进: * 1. 显示所有列(不过滤) * 2. 自定义切点UI更友好 * 3. 提供示例说明 */ import React, { useState } from 'react'; import { Modal, Select, Input, Button, Radio, Space, Tag, App, Alert } from 'antd'; import { Info } from 'lucide-react'; interface BinningDialogProps { visible: boolean; columns: Array<{ id: string; name: string; type?: string }>; sessionId: string | null; onClose: () => void; onApply: (newData: any[]) => void; } const BinningDialog: React.FC = ({ visible, columns, sessionId, onClose, onApply, }) => { const { message } = App.useApp(); const [selectedColumn, setSelectedColumn] = useState(''); const [method, setMethod] = useState<'custom' | 'equal_width' | 'equal_freq'>('equal_width'); const [newColumnName, setNewColumnName] = useState(''); // 自定义切点(改进:只存储切点值,标签自动生成) const [customBins, setCustomBins] = useState('18, 60'); const [customLabels, setCustomLabels] = useState('青少年, 成年, 老年'); // ✅ 重要:2个切点 → 3个区间 → 3个标签 // 等宽/等频 const [numBins, setNumBins] = useState(3); const [autoLabels, setAutoLabels] = useState(['低', '中', '高']); const [loading, setLoading] = useState(false); // 更新列选择 const handleColumnChange = (value: string) => { setSelectedColumn(value); const column = columns.find((c) => c.id === value); if (column) { setNewColumnName(`${column.name}_分组`); } }; // 执行分箱 const handleApply = async () => { if (!sessionId || !selectedColumn) { message.error('请选择列'); return; } if (!newColumnName) { message.warning('请输入新列名'); return; } let params: any = { column: selectedColumn, method, newColumnName, }; if (method === 'custom') { // 解析切点(支持中英文逗号) const binsInput = customBins.replace(/,/g, ','); // 中文逗号转英文 const binsArray = binsInput.split(',').map(b => parseFloat(b.trim())).filter(b => !isNaN(b)); if (binsArray.length < 1) { message.warning('至少需要1个切点(如:60 表示分为≤60和>60两组)'); return; } // 检查是否升序 const sorted = [...binsArray].sort((a, b) => a - b); if (JSON.stringify(binsArray) !== JSON.stringify(sorted)) { message.warning('切点必须按从小到大排列'); return; } // 解析标签(支持中英文逗号) const labelsInput = customLabels.replace(/,/g, ','); // 中文逗号转英文 const labelsArray = labelsInput.split(',').map(l => l.trim()).filter(l => l); // 切点数量 + 1 = 区间数量 = 标签数量 const expectedLabelCount = binsArray.length + 1; if (labelsArray.length > 0 && labelsArray.length !== expectedLabelCount) { message.warning(`需要${expectedLabelCount}个标签(${binsArray.length}个切点会生成${expectedLabelCount}个区间),或留空自动生成`); return; } params.bins = binsArray; params.labels = labelsArray.length > 0 ? labelsArray : undefined; } else { // 等宽/等频 params.numBins = numBins; // 解析标签 const labelsArray = autoLabels.filter(l => l); if (labelsArray.length > 0 && labelsArray.length !== numBins) { message.warning(`需要${numBins}个标签,或留空自动生成`); return; } if (labelsArray.length > 0) { params.labels = labelsArray; } } setLoading(true); try { const response = await fetch('/api/v1/dc/tool-c/quick-action', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId, action: 'binning', params, }), }); const result = await response.json(); if (result.success) { message.success('分箱成功!'); onApply(result.data.newDataPreview); onClose(); } else { message.error({ content: result.error || '分箱失败', duration: 5, }); } } catch (error: any) { console.error('[BinningDialog] 执行失败:', error); message.error({ content: '网络错误,请检查服务是否正常运行', duration: 5, }); } finally { setLoading(false); } }; return (
{/* 选择列 */}
{ setNumBins(value); if (value === 3) { setAutoLabels(['低', '中', '高']); } else if (value === 4) { setAutoLabels(['低', '中低', '中高', '高']); } else if (value === 5) { setAutoLabels(['极低', '低', '中', '高', '极高']); } else { setAutoLabels(Array.from({ length: value }, (_, i) => `组${i + 1}`)); } }} style={{ width: '100%' }} options={[ { value: 2, label: '2组(二分类)' }, { value: 3, label: '3组(低、中、高)' }, { value: 4, label: '4组(四分位)' }, { value: 5, label: '5组(五分类)' }, ]} />
{autoLabels.map((label, index) => ( {label} ))}
)} {/* 自定义切点配置(改进版) */} {method === 'custom' && (
切点:用逗号分隔的数字(支持中英文逗号),如 18, 6018,60
结果:自动添加边界,生成3组(≤18、18-60、>60)
标签:可选,用逗号分隔,如 青少年, 成年, 老年
注意切点数+1=标签数(如2个切点需要3个标签)
💡 示例:输入 60 → 生成2组(≤60、>60)
} type="info" showIcon icon={} className="mb-3" />
setCustomBins(e.target.value)} />
系统会自动添加最小值和最大值作为边界
setCustomLabels(e.target.value)} />
重要:标签数量 = 切点数量 + 1。留空则使用默认区间标签
)} {/* 新列名 */}
setNewColumnName(e.target.value)} />
)} {/* 操作按钮 */}
); }; export default BinningDialog;