|
| 1 | +const ArrayStats = require('./lib/ArrayStats'); |
| 2 | + |
| 3 | +exports.stats = async (event) => { |
| 4 | + const data = typeof event === 'string' ? JSON.parse(event) : event; |
| 5 | + const response = []; |
| 6 | + |
| 7 | + try { |
| 8 | + for (const point of data.points) { |
| 9 | + const arrayStats = new ArrayStats(point.results); |
| 10 | + const stats = { |
| 11 | + _min: data.min, |
| 12 | + _max: data.max, |
| 13 | + mean: arrayStats.mean(), |
| 14 | + median: arrayStats.median(), |
| 15 | + mode: arrayStats.mode(), |
| 16 | + range: arrayStats.range(), |
| 17 | + standard_deviation: arrayStats.standardDeviation(), |
| 18 | + frequency: arrayStats.frequency(), |
| 19 | + min: arrayStats.min(), |
| 20 | + max: arrayStats.max(), |
| 21 | + maxGraphDataValue: 0, |
| 22 | + graphData: arrayStats.graphData(data.min, data.max) |
| 23 | + }; |
| 24 | + |
| 25 | + let maxGraphDataValue = Math.max(...stats.graphData.map(item => item.value)); |
| 26 | + stats.maxGraphDataValue = maxGraphDataValue; |
| 27 | + |
| 28 | + let x = null; |
| 29 | + if (maxGraphDataValue <= 0.0001) x = 10000; |
| 30 | + else if (maxGraphDataValue <= 0.001) x = 1000; |
| 31 | + else if (maxGraphDataValue <= 0.01) x = 100; |
| 32 | + else if (maxGraphDataValue <= 0.1) x = 10; |
| 33 | + |
| 34 | + if (x) { |
| 35 | + stats.graphData = stats.graphData.map(item => ({ |
| 36 | + ...item, |
| 37 | + value: item.value * x |
| 38 | + })); |
| 39 | + } |
| 40 | + |
| 41 | + delete point.results; |
| 42 | + point.stats = stats; |
| 43 | + } |
| 44 | + |
| 45 | + response.push(...data.points); |
| 46 | + } catch (e) { |
| 47 | + console.error(e); |
| 48 | + } |
| 49 | + |
| 50 | + return response; |
| 51 | +}; |
| 52 | + |
| 53 | +exports.info = async () => { |
| 54 | + return { |
| 55 | + NODE_VERSION: process.version, |
| 56 | + __dirname: __dirname |
| 57 | + }; |
| 58 | +}; |
| 59 | + |
| 60 | +exports.itemAnalysis = async (event) => { |
| 61 | + const data = typeof event === 'string' ? JSON.parse(event) : event; |
| 62 | + |
| 63 | + // Create matrix |
| 64 | + const matrix = {}; |
| 65 | + data.esssr.forEach(id => { |
| 66 | + matrix[id] = {}; |
| 67 | + data.question_ids.forEach(qId => { |
| 68 | + matrix[id][qId] = 0; |
| 69 | + }); |
| 70 | + }); |
| 71 | + |
| 72 | + // Fill matrix |
| 73 | + data.esssrq.forEach(item => { |
| 74 | + matrix[item.esss_id_row_index][item.question_id] = 1; |
| 75 | + }); |
| 76 | + |
| 77 | + // Convert to array and sort by sum descending |
| 78 | + const matrixArray = Object.entries(matrix).map(([key, value]) => ({ |
| 79 | + id: key, |
| 80 | + values: value, |
| 81 | + sum: Object.values(value).reduce((a, b) => a + b, 0) |
| 82 | + })).sort((a, b) => b.sum - a.sum); |
| 83 | + |
| 84 | + // Get top and bottom 27% |
| 85 | + const count = Math.floor(matrixArray.length * 0.27); |
| 86 | + const top27 = matrixArray.slice(0, count); |
| 87 | + const bottom27 = matrixArray.slice(-count); |
| 88 | + |
| 89 | + // Calculate statistics for each question |
| 90 | + const items = data.question_ids.map(qId => { |
| 91 | + const question = data.questions.find(q => q.id === qId); |
| 92 | + const columnTotal = matrixArray.map(row => row.values[qId]); |
| 93 | + const columnTop27 = top27.map(row => row.values[qId]); |
| 94 | + const columnBottom27 = bottom27.map(row => row.values[qId]); |
| 95 | + |
| 96 | + const totalSum = columnTotal.reduce((a, b) => a + b, 0); |
| 97 | + const totalSumPersentage = (totalSum / matrixArray.length) * 100; |
| 98 | + const totalSumTop27 = columnTop27.reduce((a, b) => a + b, 0); |
| 99 | + const totalSumBottom27 = columnBottom27.reduce((a, b) => a + b, 0); |
| 100 | + const countTop27 = top27.length; |
| 101 | + const countBottom27 = bottom27.length; |
| 102 | + |
| 103 | + const Pj = (totalSumTop27 + totalSumBottom27) / (countTop27 + countBottom27); |
| 104 | + const Sj2 = Pj * (1 - Pj); |
| 105 | + const SS = Math.sqrt(Sj2); |
| 106 | + const rjx = (totalSumTop27 - totalSumBottom27) / countTop27; |
| 107 | + |
| 108 | + const arrayStats = new ArrayStats(columnTotal); |
| 109 | + |
| 110 | + return { |
| 111 | + question_id: qId, |
| 112 | + question: question, |
| 113 | + data: { |
| 114 | + totalSum: { |
| 115 | + id: 'totalSum', |
| 116 | + title: 'Maddeyi toplam doğru cevaplayan öğrenci sayısı', |
| 117 | + value: totalSum |
| 118 | + }, |
| 119 | + totalSumPersentage: { |
| 120 | + id: 'totalSumPersentage', |
| 121 | + title: 'Madde başarı yüzdesi', |
| 122 | + value: totalSumPersentage |
| 123 | + }, |
| 124 | + totalSumTop27: { |
| 125 | + id: 'totalSumTop27', |
| 126 | + title: 'Maddeyi üst grupta doğru cevaplayan öğrenci sayısı', |
| 127 | + value: totalSumTop27 |
| 128 | + }, |
| 129 | + totalSumBottom27: { |
| 130 | + id: 'totalSumBottom27', |
| 131 | + title: 'Maddeyi alt grupta doğru cevaplayan öğrenci sayısı', |
| 132 | + value: totalSumBottom27 |
| 133 | + }, |
| 134 | + Pj: { |
| 135 | + id: 'Pj', |
| 136 | + title: 'Madde güçlük indeksi', |
| 137 | + value: Pj |
| 138 | + }, |
| 139 | + Sj2: { |
| 140 | + id: 'Sj2', |
| 141 | + title: 'Madde varyansı', |
| 142 | + value: Sj2 |
| 143 | + }, |
| 144 | + rjx: { |
| 145 | + id: 'rjx', |
| 146 | + title: 'Madde ayırıcılık gücü', |
| 147 | + value: rjx |
| 148 | + }, |
| 149 | + SS: { |
| 150 | + id: 'SS', |
| 151 | + title: 'Standart sapma', |
| 152 | + value: SS |
| 153 | + }, |
| 154 | + ri: { |
| 155 | + id: 'ri', |
| 156 | + title: 'Madde güvenirlik indeksi', |
| 157 | + value: rjx * SS |
| 158 | + }, |
| 159 | + stats: { |
| 160 | + _min: 0, |
| 161 | + _max: 1, |
| 162 | + mean: arrayStats.mean(), |
| 163 | + median: arrayStats.median(), |
| 164 | + mode: arrayStats.mode(), |
| 165 | + range: arrayStats.range(), |
| 166 | + variance: arrayStats.variance(), |
| 167 | + standard_deviation: arrayStats.standardDeviation(), |
| 168 | + frequency: arrayStats.frequency(), |
| 169 | + min: arrayStats.min(), |
| 170 | + max: arrayStats.max(), |
| 171 | + maxGraphDataValue: 1, |
| 172 | + graphData: arrayStats.graphData(0, 1) |
| 173 | + } |
| 174 | + } |
| 175 | + }; |
| 176 | + }); |
| 177 | + |
| 178 | + // Calculate graph data categories |
| 179 | + const graphData = [ |
| 180 | + { |
| 181 | + id: 1, |
| 182 | + name: '[Pj>0.90]', |
| 183 | + value: 0, |
| 184 | + desc: 'Eğer etkili bir öğretim varsa tercih edilir' |
| 185 | + }, |
| 186 | + { |
| 187 | + id: 2, |
| 188 | + name: '[Pj>=0.60][rjx>=0.20]', |
| 189 | + value: 0, |
| 190 | + desc: 'Tipik iyi bir madde' |
| 191 | + }, |
| 192 | + { |
| 193 | + id: 3, |
| 194 | + name: '[Pj>=0.60][rjx<0.20]', |
| 195 | + value: 0, |
| 196 | + desc: 'Üzerinde çalışılması gereken madde' |
| 197 | + }, |
| 198 | + { |
| 199 | + id: 4, |
| 200 | + name: '[Pj<0.60][rjx>=0.20]', |
| 201 | + value: 0, |
| 202 | + desc: 'Zor fakat ayırt edici bir madde (Eğer yüksek standartlara sahipseniz bu soru iyidir)' |
| 203 | + }, |
| 204 | + { |
| 205 | + id: 5, |
| 206 | + name: '[Pj<0.60][rjx<0.20]', |
| 207 | + value: 0, |
| 208 | + desc: 'Zor ve ayırt edici olmayan madde (Bu madde kullanılamaz)' |
| 209 | + } |
| 210 | + ]; |
| 211 | + |
| 212 | + // Calculate Pj values and update graph data |
| 213 | + const Pj = []; |
| 214 | + items.forEach(item => { |
| 215 | + Pj.push(item.data.Pj.value); |
| 216 | + const pjValue = item.data.Pj.value; |
| 217 | + const rjxValue = item.data.rjx.value; |
| 218 | + |
| 219 | + if (pjValue > 0.90) { |
| 220 | + graphData[0].value++; |
| 221 | + } else if (pjValue >= 0.60 && rjxValue >= 0.20) { |
| 222 | + graphData[1].value++; |
| 223 | + } else if (pjValue >= 0.60 && rjxValue < 0.20) { |
| 224 | + graphData[2].value++; |
| 225 | + } else if (pjValue < 0.60 && rjxValue >= 0.20) { |
| 226 | + graphData[3].value++; |
| 227 | + } else if (pjValue < 0.60 && rjxValue < 0.20) { |
| 228 | + graphData[4].value++; |
| 229 | + } |
| 230 | + }); |
| 231 | + |
| 232 | + return { |
| 233 | + PjAvg: Pj.reduce((a, b) => a + b, 0) / Pj.length, |
| 234 | + studentCount: data.esssr.length, |
| 235 | + questionCount: data.question_ids.length, |
| 236 | + graphData: graphData, |
| 237 | + items: items |
| 238 | + }; |
| 239 | +}; |
0 commit comments