|
| 1 | +<!DOCTYPE html> |
| 2 | +<html lang="en"> |
| 3 | +<head> |
| 4 | + <meta charset="UTF-8" /> |
| 5 | + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 6 | + <title>tsb — nunique / any / all</title> |
| 7 | + <style> |
| 8 | + body { font-family: system-ui, sans-serif; max-width: 900px; margin: 40px auto; padding: 0 20px; background: #f9fafb; color: #1a1a2e; } |
| 9 | + h1 { color: #4f46e5; } |
| 10 | + h2 { color: #374151; border-bottom: 2px solid #e5e7eb; padding-bottom: 6px; } |
| 11 | + .demo { background: white; border: 1px solid #e5e7eb; border-radius: 8px; padding: 20px; margin: 16px 0; } |
| 12 | + pre { background: #1e1e2e; color: #cdd6f4; padding: 16px; border-radius: 6px; overflow-x: auto; font-size: 14px; } |
| 13 | + .output { background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 6px; padding: 12px; font-family: monospace; white-space: pre; margin-top: 10px; } |
| 14 | + a { color: #4f46e5; } |
| 15 | + </style> |
| 16 | +</head> |
| 17 | +<body> |
| 18 | + <p><a href="index.html">← tsb playground</a></p> |
| 19 | + <h1>🔢 nunique / any / all</h1> |
| 20 | + <p> |
| 21 | + Count unique values and perform boolean reductions, mirroring |
| 22 | + <a href="https://pandas.pydata.org/docs/reference/api/pandas.Series.nunique.html"><code>Series.nunique()</code></a>, |
| 23 | + <a href="https://pandas.pydata.org/docs/reference/api/pandas.Series.any.html"><code>Series.any()</code></a>, and |
| 24 | + <a href="https://pandas.pydata.org/docs/reference/api/pandas.Series.all.html"><code>Series.all()</code></a>. |
| 25 | + </p> |
| 26 | + |
| 27 | + <h2>1 · nunique — count distinct values</h2> |
| 28 | + <div class="demo"> |
| 29 | + <pre>import { Series, nuniqueSeries } from "tsb"; |
| 30 | + |
| 31 | +const s = new Series({ data: [1, 2, 2, 3, 3, 3, null] }); |
| 32 | + |
| 33 | +nuniqueSeries(s); // 3 (null excluded by default) |
| 34 | +nuniqueSeries(s, { dropna: false }); // 4 (null counted as a distinct value)</pre> |
| 35 | + <div class="output">nuniqueSeries(s) → 3 |
| 36 | +nuniqueSeries(s, {dropna:false}) → 4</div> |
| 37 | + </div> |
| 38 | + |
| 39 | + <h2>2 · any — is any element truthy?</h2> |
| 40 | + <div class="demo"> |
| 41 | + <pre>import { anySeries } from "tsb"; |
| 42 | + |
| 43 | +const allZero = new Series({ data: [0, 0, 0] }); |
| 44 | +const hasOne = new Series({ data: [0, 0, 1] }); |
| 45 | + |
| 46 | +anySeries(allZero); // false |
| 47 | +anySeries(hasOne); // true |
| 48 | + |
| 49 | +// With nulls (skipna=true by default) |
| 50 | +const withNull = new Series({ data: [null, 0, null] }); |
| 51 | +anySeries(withNull); // false — null skipped, 0 is falsy</pre> |
| 52 | + <div class="output">anySeries(allZero) → false |
| 53 | +anySeries(hasOne) → true |
| 54 | +anySeries(withNull) → false</div> |
| 55 | + </div> |
| 56 | + |
| 57 | + <h2>3 · all — are all elements truthy?</h2> |
| 58 | + <div class="demo"> |
| 59 | + <pre>import { allSeries } from "tsb"; |
| 60 | + |
| 61 | +const allTrue = new Series({ data: [1, 2, 3] }); |
| 62 | +const hasFalsy = new Series({ data: [1, 0, 3] }); |
| 63 | + |
| 64 | +allSeries(allTrue); // true |
| 65 | +allSeries(hasFalsy); // false |
| 66 | + |
| 67 | +// Empty or all-null series vacuously returns true |
| 68 | +allSeries(new Series({ data: [] })); // true |
| 69 | +allSeries(new Series({ data: [null, null] })); // true</pre> |
| 70 | + <div class="output">allSeries(allTrue) → true |
| 71 | +allSeries(hasFalsy) → false |
| 72 | +allSeries([]) → true (vacuous) |
| 73 | +allSeries([null]) → true (vacuous)</div> |
| 74 | + </div> |
| 75 | + |
| 76 | + <h2>4 · DataFrame nunique</h2> |
| 77 | + <div class="demo"> |
| 78 | + <pre>import { DataFrame, nuniqueDataFrame } from "tsb"; |
| 79 | + |
| 80 | +const df = DataFrame.fromColumns({ |
| 81 | + category: ["A", "B", "A", "C"], |
| 82 | + value: [1, 2, 1, 3 ], |
| 83 | +}); |
| 84 | + |
| 85 | +nuniqueDataFrame(df); // per-column: category→3, value→3 |
| 86 | +nuniqueDataFrame(df, { axis: 1 }); // per-row: how many distinct values in each row</pre> |
| 87 | + <div class="output">nuniqueDataFrame(df) → category: 3, value: 3 |
| 88 | +nuniqueDataFrame(df, {axis:1}) → row0: 2, row1: 2, row2: 2, row3: 2</div> |
| 89 | + </div> |
| 90 | + |
| 91 | + <h2>5 · DataFrame any / all</h2> |
| 92 | + <div class="demo"> |
| 93 | + <pre>import { anyDataFrame, allDataFrame } from "tsb"; |
| 94 | + |
| 95 | +const df2 = DataFrame.fromColumns({ |
| 96 | + a: [0, 0, 1], |
| 97 | + b: [1, 1, 1], |
| 98 | +}); |
| 99 | + |
| 100 | +anyDataFrame(df2); // a: true, b: true (each col has at least one truthy) |
| 101 | +allDataFrame(df2); // a: false, b: true (col a has a 0) |
| 102 | + |
| 103 | +// axis=1: reduce across columns per row |
| 104 | +anyDataFrame(df2, { axis: 1 }); // row0: true, row1: true, row2: true |
| 105 | +allDataFrame(df2, { axis: 1 }); // row0: false, row1: false, row2: true</pre> |
| 106 | + <div class="output">anyDataFrame(df2) → a: true, b: true |
| 107 | +allDataFrame(df2) → a: false, b: true |
| 108 | +anyDataFrame(df2,{axis:1}) → [true, true, true] |
| 109 | +allDataFrame(df2,{axis:1}) → [false, false, true]</div> |
| 110 | + </div> |
| 111 | +</body> |
| 112 | +</html> |
0 commit comments