@@ -74,6 +74,208 @@ function safeProcess(data) {
7474}
7575```
7676
77+ ### ✅ Optimizations Based on TinyFrame Experience
78+
79+ #### Efficient Array Handling
80+
81+ - ** Use typed arrays** (` Float64Array ` , ` Uint32Array ` ) for numeric data instead of regular JavaScript arrays.
82+ - ** Avoid data copying** — use references or in-place operations where possible.
83+ - ** Pre-allocate memory** for result arrays in a single call, knowing the size in advance.
84+ - ** Use array pooling** for temporary arrays to reduce garbage collector pressure.
85+
86+ ``` js
87+ // Bad
88+ const result = [];
89+ for (let i = 0 ; i < data .length ; i++ ) {
90+ result .push (data[i] * 2 );
91+ }
92+
93+ // Good
94+ const result = new Float64Array (data .length );
95+ for (let i = 0 ; i < data .length ; i++ ) {
96+ result[i] = data[i] * 2 ;
97+ }
98+ ```
99+
100+ #### Algorithmic Optimizations
101+
102+ - ** Avoid nested loops** — aim for O(n) complexity instead of O(n²).
103+ - ** Use sliding windows** instead of recalculating for overlapping data ranges.
104+ - ** Apply prefix-sum** for efficient calculation of sliding statistics on large windows.
105+ - ** Cache intermediate results** to avoid repeated calculations.
106+
107+ ``` js
108+ // Bad (O(n*k))
109+ function rollingSum (values , windowSize ) {
110+ const result = new Float64Array (values .length - windowSize + 1 );
111+ for (let i = 0 ; i <= values .length - windowSize; i++ ) {
112+ let sum = 0 ;
113+ for (let j = 0 ; j < windowSize; j++ ) {
114+ sum += values[i + j];
115+ }
116+ result[i] = sum;
117+ }
118+ return result;
119+ }
120+
121+ // Good (O(n))
122+ function rollingSum (values , windowSize ) {
123+ const result = new Float64Array (values .length - windowSize + 1 );
124+ let sum = 0 ;
125+
126+ // Initialize first window
127+ for (let i = 0 ; i < windowSize; i++ ) {
128+ sum += values[i];
129+ }
130+ result[0 ] = sum;
131+
132+ // Sliding window
133+ for (let i = 1 ; i <= values .length - windowSize; i++ ) {
134+ sum = sum - values[i - 1 ] + values[i + windowSize - 1 ];
135+ result[i] = sum;
136+ }
137+ return result;
138+ }
139+ ```
140+
141+ #### Efficient NaN and Invalid Value Handling
142+
143+ - ** Use counters for invalid values** instead of repeated ` isNaN() ` checks.
144+ - ** Apply validity masks** for filtering NaN values in a single pass.
145+ - ** Avoid checks on each iteration** — group checks and perform them in advance.
146+
147+ ``` js
148+ // Bad
149+ function hasNaN (array ) {
150+ for (let i = 0 ; i < array .length ; i++ ) {
151+ if (isNaN (array[i])) return true ;
152+ }
153+ return false ;
154+ }
155+
156+ // Good
157+ function countNaN (array ) {
158+ let badCount = 0 ;
159+ for (let i = 0 ; i < array .length ; i++ ) {
160+ if (isNaN (array[i])) badCount++ ;
161+ }
162+ return badCount;
163+ }
164+ ```
165+
166+ #### Hashing and Duplicate Detection
167+
168+ - ** Avoid using ` JSON.stringify ` ** for data serialization — use efficient hash functions (FNV-1a, Murmur3).
169+ - ** Use hash tables with open addressing** instead of Map for large datasets.
170+ - ** Pre-compute hashes** for reused values.
171+
172+ ``` js
173+ // Bad
174+ function findDuplicates (rows , keyColumns ) {
175+ const seen = new Set ();
176+ return rows .filter ((row ) => {
177+ const key = JSON .stringify (keyColumns .map ((col ) => row[col]));
178+ if (seen .has (key)) return true ;
179+ seen .add (key);
180+ return false ;
181+ });
182+ }
183+
184+ // Good
185+ function hashRow (row , keyColumns ) {
186+ let hash = 2166136261 ; // FNV-1a offset basis
187+ for (const col of keyColumns) {
188+ const val = row[col];
189+ const str = String (val);
190+ for (let i = 0 ; i < str .length ; i++ ) {
191+ hash ^= str .charCodeAt (i);
192+ hash = (hash * 16777619 ) >>> 0 ; // FNV prime
193+ }
194+ }
195+ return hash;
196+ }
197+
198+ function findDuplicates (rows , keyColumns ) {
199+ const seen = new Set ();
200+ return rows .filter ((row ) => {
201+ const hash = hashRow (row, keyColumns);
202+ if (seen .has (hash)) return true ;
203+ seen .add (hash);
204+ return false ;
205+ });
206+ }
207+ ```
208+
209+ #### Vectorization and Parallelism
210+
211+ - ** Use block processing** for better vectorization in V8.
212+ - ** Split large tasks** into subtasks for parallel processing.
213+ - ** Consider using Web Workers** for CPU-intensive operations.
214+
215+ #### General Performance Recommendations
216+
217+ - ** Measure before optimizing** — use profiling to identify bottlenecks.
218+ - ** Set performance budgets** for critical operations.
219+ - ** Test on realistic data volumes** — optimizations may only show up on large datasets.
220+ - ** Avoid premature optimization** — first achieve correctness, then optimize critical paths.
221+
222+ ## 📊 Работа с данными и тестирование
223+
224+ ### ✅ Обработка специальных значений
225+
226+ При работе с числовыми данными важно четко определить и документировать, как библиотека обрабатывает специальные значения:
227+
228+ - ** ` null ` ** - преобразуется в ` 0 ` в числовых колонках
229+ - ** ` undefined ` ** - преобразуется в ` NaN ` в числовых колонках
230+ - ** ` NaN ` ** - сохраняется как ` NaN `
231+
232+ ### ✅ Сохранение исходных данных
233+
234+ - ** Сохраняйте "сырые" значения** - храните оригинальные данные рядом с оптимизированными для вычислений
235+ - ** Используйте маски валидности** - отслеживайте, где были ` undefined ` и другие специальные значения
236+ - ** Разделяйте данные и метаданные** - не теряйте информацию при оптимизации
237+
238+ ``` js
239+ // Рекомендуемый подход
240+ export function createFrame (data ) {
241+ const columns = {}; // оптимизированные данные
242+ const rawColumns = {}; // исходные данные
243+ // ...
244+
245+ return { columns, rawColumns, rowCount, columnNames };
246+ }
247+ ```
248+
249+ ### ✅ Явные значения по умолчанию
250+
251+ - ** Документируйте поведение по умолчанию** - например, какой тип стандартного отклонения (популяционное или выборочное) используется
252+ - ** Избегайте неоднозначных дефолтов** - они приводят к разным ожиданиям в тестах
253+ - ** Выносите правила преобразования в отдельные функции** - например, ` normalizeNumeric(value) `
254+
255+ ### ✅ Тестирование
256+
257+ - ** Тест-кейсы должны быть согласованы** - они не должны противоречить друг другу
258+ - ** Документируйте ожидаемое поведение** - особенно для обработки специальных значений
259+ - ** Избегайте специальных обработок для тестов** - функции должны работать универсально
260+
261+ ``` js
262+ // Плохо: специальная обработка для конкретного теста
263+ if (values .length === 6 && values[0 ] === 1 && Number .isNaN (values[1 ])) {
264+ return 1.92 ; // Магическое число для теста
265+ }
266+
267+ // Хорошо: универсальный алгоритм, который работает для всех случаев
268+ function calculateStandardDeviation (values , population = true ) {
269+ // Универсальный алгоритм...
270+ }
271+ ```
272+
273+ ### ✅ Оптимизация вычислений
274+
275+ - ** Избегайте двойных проходов** - не делайте отдельную валидацию, если типы уже проверены
276+ - ** Доверяйте структуре данных** - если ` createFrame ` гарантирует однородность типов, не перепроверяйте это
277+ - ** Минимизируйте копирование данных** - работайте с исходными массивами, где это возможно
278+
77279## 💰 Numerical Accuracy
78280
79281### ✅ Use Integers for Money (e.g., cents)
0 commit comments