当前位置: 首页 > 工具软件 > minmax.js > 使用案例 >

深入学习D3JS:d3-array Statistics部分 d3.min, d3.max,d3.extent,d3.sum,d3.scan,d3.ascending,d3.deviation

耿永寿
2023-12-01

本着学习d3js的原则,对函数用法做出解释,对源码写出自己的理解,文章最后补充一些源码中用到的JS语法,欢迎各位批评指正,学习交流!多多评论共同进步!

d3.min(iterable[,accessor])

根据顺序返回可迭代对象中的最小值,如果没有可以比较的值,返回undefined. 可选访问函数等价于调用Array.from(Array.from用法,将类似于数组的对象转化为数组,要求该对象有length属性)。

和Math.min不同,该方法会会忽略undefined,null,NaN值,这对处理missing data有用, 此外,元素比较顺序依赖字符串顺序而不是数字顺序,比如,["20","3"]按照字符串顺序最小值为“20”,[20,3]按照数字顺序最小值为3。

export default function min(values, valueof) {
  let min;
  if (valueof === undefined) {
    for (let value of values) {
      if (value != null
          && value >= value
          && (min === undefined || min > value)) {
        min = value;
      }
    }
  } else {
    let index = -1;
    for (let value of values) {
      if ((value = valueof(value, ++index, values)) != null
          && value >= value
          && (min === undefined || min > value)) {
        min = value;
      }
    }
  }
  return min;
}

采用ES6 module标准导出接口,这里value >= value 操作有点不解?

d3.max(iterable[, accessor]) 

该方法返回最大值,其他特性与d3.min类似。

export default function max(values, valueof) {
  let max;
  if (valueof === undefined) {
    for (let value of values) {
      if (value != null
          && value >= value
          && (max === undefined || max < value)) {
        max = value;
      }
    }
  } else {
    let index = -1;
    for (let value of values) {
      if ((value = valueof(value, ++index, values)) != null
          && value >= value
          && (max === undefined || max < value)) {
        max = value;
      }
    }
  }
  return max;
}

d3.extent(iterable[, accessor])

该方法返回[最小值,最大值],没有可比较元素返回[undefined,undefined]。

export default function(values, valueof) {
  let min;
  let max;
  if (valueof === undefined) {
    for (let value of values) {
      if (value != null && value >= value) {
        if (min === undefined) {
          min = max = value;
        } else {
          if (min > value) min = value;
          if (max < value) max = value;
        }
      }
    }
  } else {
    let index = -1;
    for (let value of values) {
      if ((value = valueof(value, ++index, values)) != null && value >= value) {
        if (min === undefined) {
          min = max = value;
        } else {
          if (min > value) min = value;
          if (max < value) max = value;
        }
      }
    }
  }
  return [min, max];
}

实现原理没有什么特别的,第一次循环将value赋值min,max,后面循环求出最大值最小值。

 d3.sum(iterable[, accessor]) 

返回可迭代元素和,如果没有元素返回0。该方法忽略undefine,NaN。

export default function sum(values, valueof) {
  let sum = 0;
  if (valueof === undefined) {
    for (let value of values) {
      if (value = +value) {
        sum += value;
      }
    }
  } else {
    let index = -1;
    for (let value of values) {
      if (value = +valueof(value, ++index, values)) {
        sum += value;
      }
    }
  }
  return sum;
}

这里value前的+号,作用是什么?

d3.mean(iterable[, accessor])

返回平均值,忽略undefine,NaN。

export default function mean(values, valueof) {
  let count = 0;
  let sum = 0;
  if (valueof === undefined) {
    for (let value of values) {
      if (value != null && (value = +value) >= value) {
        ++count, sum += value;
      }
    }
  } else {
    let index = -1;
    for (let value of values) {
      if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) {
        ++count, sum += value;
      }
    }
  }
  if (count) return sum / count;
}

d3.median(iterable[, accessor])

使用r-7method返回中位数

import quantile from "./quantile.js";
import quickselect from "./quickselect.js";

function* numbers(values, valueof) {
  if (valueof === undefined) {
    for (let value of values) {
      if (value != null && (value = +value) >= value) {
        yield value;
      }
    }
  } else {
    let index = -1;
    for (let value of values) {
      if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) {
        yield value;
      }
    }
  }
}

export default function(values, valueof) {
  values = Float64Array.from(numbers(values, valueof));
  if (!values.length) return;
  const n = values.length;
  const i = n >> 1;
  quickselect(values, i - 1, 0);
  if ((n & 1) === 0) quickselect(values, i, i);
  return quantile(values, 0.5);
}

首先通过迭代器函数生成float64数组,通过quickselect给中值排好序。

d3.quickselect(arraykleft = 0, right = array.length - 1, compare = ascending)

该方法为Floyd–Rivest algorithm,快速选择算法。

详情:https://en.wikipedia.org/wiki/Floyd%E2%80%93Rivest_algorithm

import ascending from "./ascending.js";

// Based on https://github.com/mourner/quickselect
// ISC license, Copyright 2018 Vladimir Agafonkin.
export default function quickselect(array, k, left = 0, right = array.length - 1, compare = ascending) {
  while (right > left) {
    if (right - left > 600) {
      const n = right - left + 1;
      const m = k - left + 1;
      const z = Math.log(n);
      const s = 0.5 * Math.exp(2 * z / 3);
      const sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
      const newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
      const newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
      quickselect(array, k, newLeft, newRight, compare);
    }

    const t = array[k];
    let i = left;
    let j = right;

    swap(array, left, k);
    if (compare(array[right], t) > 0) swap(array, left, right);

    while (i < j) {
      swap(array, i, j), ++i, --j;
      while (compare(array[i], t) < 0) ++i;
      while (compare(array[j], t) > 0) --j;
    }

    if (compare(array[left], t) === 0) swap(array, left, j);
    else ++j, swap(array, j, right);

    if (j <= k) left = j + 1;
    if (k <= j) right = j - 1;
  }
  return array;
}

function swap(array, i, j) {
  const t = array[i];
  array[i] = array[j];
  array[j] = t;
}

这个算法有点复杂,功能为快速选择k值的算法。如果数据量过大,会先选择一个样本进行就行搜索。首先对第在k的位置上的值放置到该值应该呆的位置,之后根据k位置的值所在的下标进行更新,取前部或着取后部,循环迭代直到找到了第k值。

number.js

x为null返回NaN,否则返回x,该函数的功能就是可以把字符串变成数字,+“123”=123。

export default function(x) {
  return x === null ? NaN : +x;
}

d3.quantile(arrayp[, accessor])

    
import number from "./number.js";

export default function quantile(values, p, valueof = number) {
  if (!(n = values.length)) return;
  if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values);
  if (p >= 1) return +valueof(values[n - 1], n - 1, values);
  var n,
      i = (n - 1) * p,
      i0 = Math.floor(i),
      value0 = +valueof(values[i0], i0, values),
      value1 = +valueof(values[i0 + 1], i0 + 1, values);
  return value0 + (value1 - value0) * (i - i0);
}

用法如下,当然要求传入的数组为排好序的数组

var a = [0, 10, 30];
d3.quantile(a, 0); // 0
d3.quantile(a, 0.5); // 10
d3.quantile(a, 1); // 30
d3.quantile(a, 0.25); // 5
d3.quantile(a, 0.75); // 20
d3.quantile(a, 0.1); // 2

d3.variance(iterable[, accessor]) 

export default function variance(values, valueof) {
  let count = 0;
  let delta;
  let mean = 0;
  let sum = 0;
  if (valueof === undefined) {
    for (let value of values) {
      if (value != null && (value = +value) >= value) {
        delta = value - mean;
        mean += delta / ++count;
        sum += delta * (value - mean);
      }
    }
  } else {
    let index = -1;
    for (let value of values) {
      if ((value = valueof(value, ++index, values)) != null && (value = +value) >= value) {
        delta = value - mean;
        mean += delta / ++count;
        sum += delta * (value - mean);
      }
    }
  }
  if (count > 1) return sum / (count - 1);
}

使用增量算法计算方差:https://www.cnblogs.com/yoyaprogrammer/p/delta_variance.html

d3.deviation(iterable[, accessor])

import variance from "./variance.js";

export default function deviation(values, valueof) {
  const v = variance(values, valueof);
  return v ? Math.sqrt(v) : v;
}

标准差即对方差开根号

d3.scan(iterable[,comparator])

对可迭代对象执行线性迭代,根据可选比较函数comparator返回最小元素的索引,(如果比较函数返回NaN,即给定的对象没有可以比较的元素,该函数返回undefined,比较函数没有指定,默认为ascending).例如:

const array = [{foo: 42}, {foo: 91}];
d3.scan(array, (a, b) => a.foo - b.foo); // 0
d3.scan(array, (a, b) => b.foo - a.foo); // 1

该方法和min非常类似,区别就是该方法接受一个比较函数而不是访问函数,返回元素为下标。

import ascending from "./ascending.js";

export default function scan(values, compare = ascending) {
  let min;
  let minIndex;
  let index = -1;
  for (const value of values) {
    ++index;
    if (minIndex === undefined
        ? compare(value, value) === 0
        : compare(value, min) < 0) {
      min = value;
      minIndex = index;
    }
  }
  return minIndex;
}

d3.ascending(ab)

该比较函数为natural order,递增顺序。可以与内置的array.sort方法一起使用,以按升序排列元素。

export default function(a, b) {
  return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
}

这里要注意array.sort在没有指定比较函数的情况下,默认排序为字典顺序(lexicographic),而不是natural。

JS语法补充:

js中的iterable

ES6标准引入iterable类型,Array、Map、Set都属于iterable类型,可以使用for...of循环来遍历。

了解更多:https://www.cnblogs.com/yuanxinru321/p/7551660.html

关于Set:https://www.cnblogs.com/diligenceday/p/5479076.html

Array.from方法:https://www.jianshu.com/p/f4554f74de95

function* 生成器函数

通过next()方法调用yield,返回两个值,value和done,分别为本次的返回值和后续是否还有yield语句布尔值。yield*可以移交执行权。

了解更多:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/function*

 类似资料: