## 图表类型：营养分布散点矩阵 (Scatter Nutrients Matrix)

**生成指令**：你现在的任务是生成一个 ECharts 的 `option` 配置。请根据以下骨架代码和数据结构要求，结合用户的实际数据进行填充和修改，生成一份完整的图表配置参数。

### 外部数据结构分析
该图表需要特定的外部数据结构支撑。以下是下载后的数据结构示例，请将用户的真实数据映射为类似结构：

**数据文件 [/data/asset/data/nutrients.json]**:
```json
数据是一个数组 (Array)，共 7637 项。单项结构示例：
[
  [
    "Beverage, instant breakfast powder, chocolate, not reconstituted",
    "Dairy and Egg Products",
    19.9,
    0.285,
    0.385,
    0.4,
    0.07690000000000001,
    0.947,
    66.2,
    65.8,
    1.4,
    7.4,
    357,
    0.56,
    0.314,
    0.278,
    27481
  ],
  [
    "Beverage, instant breakfast powder, chocolate, sugar-free, not reconstituted",
    "Dairy and Egg Products",
    35.8,
    0.5,
    0.717,
    2,
    0.138,
    1.705,
    41,
    39,
    5.1,
    7.4,
    358,
    2.162,
    1.189,
    1.027,
    27482
  ]
]
```

### ECharts Option 骨架参考
请基于此结构（已剥离冗长写死的数据）生成配置。不要直接输出此骨架，而是要输出**包含真实数据和完整逻辑**的完整 `option` 对象：

```javascript
const indices = {
  name: 0,
  group: 1,
  id: 16
};
const schema = [
  { name: 'name', index: 0 },
  { name: 'group', index: 1 },
  { name: 'protein', index: 2 },
  { name: 'calcium', index: 3 },
  { name: 'sodium', index: 4 },
  { name: 'fiber', index: 5 },
  { name: 'vitaminc', index: 6 },
  { name: 'potassium', index: 7 },
  { name: 'carbohydrate', index: 8 },
  { name: 'sugars', index: 9 },
  { name: 'fat', index: 10 },
  { name: 'water', index: 11 },
  { name: 'calories', index: 12 },
  { name: 'saturated', index: 13 },
  { name: 'monounsat', index: 14 },
  { name: 'polyunsat', index: 15 },
  { name: 'id', index: 16 }
];
const axisColors = {
  xAxisLeft: '#2A8339',
  xAxisRight: '#367DA6',
  yAxisTop: '#A68B36',
  yAxisBottom: '#BD5692'
};
const colorBySchema = {};
const fieldIndices = schema.reduce(function (obj, item) {
  obj[item.name] = item.index;
  return obj;
}, {});
const groupCategories = [];
const groupColors = [];
let data;
// zlevel 为 1 的层开启尾迹特效
myChart.getZr().configLayer(1, {
  motionBlur: true
});
function normalizeData(originData) {
  let groupMap = {};
  originData.forEach(function (row) {
    let groupName = row[indices.group];
    if (!groupMap.hasOwnProperty(groupName)) {
      groupMap[groupName] = 1;
    }
  });
  originData.forEach(function (row) {
    row.forEach(function (item, index) {
      if (
        index !== indices.name &&
        index !== indices.group &&
        index !== indices.id
      ) {
        // Convert null to zero, as all of them under unit "g".
        row[index] = parseFloat(item) || 0;
      }
    });
  });
  for (let groupName in groupMap) {
    if (groupMap.hasOwnProperty(groupName)) {
      groupCategories.push(groupName);
    }
  }
  let hStep = Math.round(300 / (groupCategories.length - 1));
  for (let i = 0; i < groupCategories.length; i++) {
    groupColors.push(echarts.color.modifyHSL('#5A94DF', hStep * i));
  }
  return originData;
}
function makeAxis(dimIndex, id, name, nameLocation) {
  const axisColor = axisColors[id.split('-')[dimIndex]];
  colorBySchema[name] = axisColor;
  return {
    id: id,
    name: name,
    nameLocation: nameLocation,
    nameGap: nameLocation === 'middle' ? 30 : 10,
    gridId: id,
    splitLine: { show: false },
    axisLine: {
      lineStyle: {
        color: axisColor
      }
    },
    axisLabel: {
      color: axisColor
    },
    axisTick: {
      lineStyle: {
        color: axisColor
      }
    }
  };
}
function makeSeriesData(xLeftOrRight, yTopOrBottom) {
  return data.map(function (item, idx) {
    const schemaX = app.config[xLeftOrRight];
    const schemaY = app.config[yTopOrBottom];
    return [
      item[fieldIndices[schemaX]],
      item[fieldIndices[schemaY]],
      item[1],
      item[0],
      schemaX,
      schemaY,
      idx // 6
    ];
  });
}
function makeSeries(xLeftOrRight, yTopOrBottom) {
  let id = xLeftOrRight + '-' + yTopOrBottom;
  return {
    zlevel: 1,
    type: 'scatter',
    name: 'nutrients',
    xAxisId: id,
    yAxisId: id,
    symbolSize: 8,
    emphasis: {
      itemStyle: {
        color: '#fff'
      }
    },
    data: makeSeriesData(xLeftOrRight, yTopOrBottom)
  };
}
function makeDataZoom(opt) {
  return Object.assign(
    {
      type: 'slider',
      filterMode: 'empty',
      realtime: false
    },
    opt
  );
}
function tooltipFormatter(params) {
  // Remove duplicate by data name.
  let mapByDataName = {};
  let mapOnDim = {
    x: {},
    y: {},
    xy: {}
  };
  params.forEach(function (item) {
    let data = item.data;
    let dataName = data[3];
    let mapItem = mapByDataName[dataName] || (mapByDataName[dataName] = {});
    mapItem[data[4]] = data[0];
    mapItem[data[5]] = data[1];
    mapOnDim[item.axisDim][dataName] = mapItem;
  });
  Object.keys(mapByDataName).forEach(function (dataName) {
    if (mapOnDim.x[dataName] && mapOnDim.y[dataName]) {
      mapOnDim.xy[dataName] = mapByDataName[dataName];
      delete mapOnDim.x[dataName];
      delete mapOnDim.y[dataName];
    }
  });
  let resultHTML = [];
  [
    ['xy', 'CROSS'],
    ['x', 'V LINE'],
    ['y', 'H LINE']
  ].forEach(function (dimDefine) {
    let html = [];
    Object.keys(mapOnDim[dimDefine[0]]).forEach(function (dataName) {
      let mapItem = mapOnDim[dimDefine[0]][dataName];
      let valuesHTML = [];
      Object.keys(mapItem).forEach(function (dataName) {
        valuesHTML.push(
          '<span style="color:' +
            colorBySchema[dataName] +
            '">' +
            dataName +
            '</span>: ' +
            mapItem[dataName]
        );
      });
      html.push(
        '<div style="margin: 10px 0">' +
          dataName +
          '<br/>' +
          valuesHTML.join('<br/>') +
          '</div>'
      );
    });
    html.length &&
      resultHTML.push(
        '<div style="margin: 10px 0">' +
          '<div style="font-size: 16px; color: #aaa">POINTS ON ' +
          dimDefine[1] +
          '</div>' +
          html.join('') +
          '</div>'
      );
  });
  return resultHTML.join('');
}
function getOption(data) {
  let gridWidth = '35%';
  let gridHeight = '35%';
  let gridLeft = 80;
  let gridRight = 50;
  let gridTop = 50;
  let gridBottom = 80;
  return {
    tooltip: {
      trigger: 'none',
      padding: [10, 20, 10, 20],
      backgroundColor: 'rgba(0,0,0,0.7)',
      transitionDuration: 0,
      extraCssText: 'width: 300px; white-space: normal',
      textStyle: {
        color: '#fff',
        fontSize: 12
      },
      position: function (pos, params, el, elRect, size) {
        let obj = {};
        obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 60;
        obj[['top', 'bottom'][+(pos[1] < size.viewSize[1] / 2)]] = 20;
        return obj;
      },
      formatter: tooltipFormatter
    },
    axisPointer: {
      show: true,
      snap: true,
      lineStyle: {
        type: 'dashed'
      },
      label: {
        show: true,
        margin: 6,
        backgroundColor: '#556',
        color: '#fff'
      },
      link: [
        {
          xAxisId: ['xAxisLeft-yAxisTop', 'xAxisLeft-yAxisBottom']
        },
        {
          xAxisId: ['xAxisRight-yAxisTop', 'xAxisRight-yAxisBottom']
        },
        {
          yAxisId: ['xAxisLeft-yAxisTop', 'xAxisRight-yAxisTop']
        },
        {
          yAxisId: ['xAxisLeft-yAxisBottom', 'xAxisRight-yAxisBottom']
        }
      ]
    },
    xAxis: [
      makeAxis(0, 'xAxisLeft-yAxisTop', 'carbohydrate', 'middle'),
      makeAxis(0, 'xAxisLeft-yAxisBottom', 'carbohydrate', 'middle'),
      makeAxis(0, 'xAxisRight-yAxisTop', 'potassium', 'middle'),
      makeAxis(0, 'xAxisRight-yAxisBottom', 'potassium', 'middle')
    ],
    yAxis: [
      makeAxis(1, 'xAxisLeft-yAxisTop', 'calcium', 'end'),
      makeAxis(1, 'xAxisLeft-yAxisBottom', 'fiber', 'end'),
      makeAxis(1, 'xAxisRight-yAxisTop', 'calcium', 'end'),
      makeAxis(1, 'xAxisRight-yAxisBottom', 'fiber', 'end')
    ],
    grid: [
      {
        id: 'xAxisLeft-yAxisTop',
        left: gridLeft,
        top: gridTop,
        width: gridWidth,
        height: gridHeight
      },
      {
        id: 'xAxisLeft-yAxisBottom',
        left: gridLeft,
        bottom: gridBottom,
        width: gridWidth,
        height: gridHeight
      },
      {
        id: 'xAxisRight-yAxisTop',
        right: gridRight,
        top: gridTop,
        width: gridWidth,
        height: gridHeight
      },
      {
        id: 'xAxisRight-yAxisBottom',
        right: gridRight,
        bottom: gridBottom,
        width: gridWidth,
        height: gridHeight
      }
    ],
    dataZoom: [
      makeDataZoom({
        width: gridWidth,
        height: 20,
        left: gridLeft,
        bottom: 10,
        xAxisIndex: [0, 1]
      }),
      makeDataZoom({
        width: gridWidth,
        height: 20,
        right: gridRight,
        bottom: 10,
        xAxisIndex: [2, 3]
      }),
      makeDataZoom({
        orient: 'vertical',
        width: 20,
        height: gridHeight,
        left: 10,
        top: gridTop,
        yAxisIndex: [0, 2]
      }),
      makeDataZoom({
        orient: 'vertical',
        width: 20,
        height: gridHeight,
        left: 10,
        bottom: gridBottom,
        yAxisIndex: [1, 3]
      })
    ],
    visualMap: [
      {
        show: false,
        type: 'piecewise',
        categories: groupCategories,
        dimension: 2,
        inRange: {
          color: groupColors //['#d94e5d','#eac736','#50a3ba']
        },
        outOfRange: {
          color: ['#ccc'] //['#d94e5d','#eac736','#50a3ba']
        },
        top: 20,
        textStyle: {
          color: '#fff'
        },
        realtime: false
      }
    ],
    series: [
      makeSeries('xAxisLeft', 'yAxisTop'),
      makeSeries('xAxisLeft', 'yAxisBottom'),
      makeSeries('xAxisRight', 'yAxisTop'),
      makeSeries('xAxisRight', 'yAxisBottom')
    ],
    animationThreshold: 5000,
    progressiveThreshold: 5000,
    animationEasingUpdate: 'cubicInOut',
    animationDurationUpdate: 2000
  };
}
const fieldNames = schema
  .map(function (item) {
    return item.name;
  })
  .slice(2);
app.config = {
  xAxisLeft: 'carbohydrate',
  yAxisTop: 'calcium',
  xAxisRight: 'potassium',
  yAxisBottom: 'fiber',
  onChange: function () {
    if (data) {
      colorBySchema[app.config.xAxisLeft] = axisColors.xAxisLeft;
      colorBySchema[app.config.xAxisRight] = axisColors.xAxisRight;
      colorBySchema[app.config.yAxisTop] = axisColors.yAxisTop;
      colorBySchema[app.config.yAxisBottom] = axisColors.yAxisBottom;
      myChart.setOption({
        xAxis: [
          {
            name: app.config.xAxisLeft
          },
          {
            name: app.config.xAxisLeft
          },
          {
            name: app.config.xAxisRight
          },
          {
            name: app.config.xAxisRight
          }
        ],
        yAxis: [
          {
            name: app.config.yAxisTop
          },
          {
            name: app.config.yAxisBottom
          },
          {
            name: app.config.yAxisTop
          },
          {
            name: app.config.yAxisBottom
          }
        ],
        series: [
          {
            data: makeSeriesData('xAxisLeft', 'yAxisTop')
          },
          {
            data: makeSeriesData('xAxisLeft', 'yAxisBottom')
          },
          {
            data: makeSeriesData('xAxisRight', 'yAxisTop')
          },
          {
            data: makeSeriesData('xAxisRight', 'yAxisBottom')
          }
        ]
      });
    }
  }
};
app.configParameters = {
  xAxisLeft: {
    options: fieldNames
  },
  xAxisRight: {
    options: fieldNames
  },
  yAxisTop: {
    options: fieldNames
  },
  yAxisBottom: {
    options: fieldNames
  }
};
$.get(ROOT_PATH + '/data/asset/data/nutrients.json', function (originData) {
  data = normalizeData(originData).slice(0, 1000);
  myChart.setOption((option = getOption(data)));
});
```

