八、创建无代码数据分析/处理系统

创建 Danfo.js 的主要目的之一是在浏览器中轻松启用数据处理。这提供了将数据分析和数据处理无缝集成到网络应用中的能力。除了能够将数据处理添加到网络应用之外,我们还有工具可以让数据处理和分析看起来更像设计师使用 PhotoshopFigma 时所做的事情;他们如何通过点击在画布上混合笔触,或者他们如何通过在画布上放置画布,通过拖放和一些按钮点击来操作图像。

有了 Danfo.js,我们可以轻松地启用这样一个环境(使用工具,如 React.jsVue.js ),在这个环境中,数据科学家变成了艺术家,只需点击几下按钮就可以操纵数据,并获得所需的输出,而无需实际编写任何代码。

具有这种特性的工具很多,但是 Danfo.js 的酷之处在于用 JavaScript 中的工具构建整个应用。事实上,在不调用服务器的情况下在浏览器中完成所有操作是相当惊人的。

本章的目标是展示如何使用 Danfo.js 和 React.js 构建这样的环境,另外,请注意,这里使用的工具(除了 Danfo.js 之外)对于构建应用并不是强制性的;这些只是我相当熟悉的工具。

本章将涵盖以下主题:

  • 设置项目环境
  • 构建和设计应用
  • 应用布局和DataTable组件
  • 创建不同的DataFrame操作组件
  • 实现Chart组件

技术要求

以下是本章的基本环境和知识要求:

设置项目环境

React.js 用于项目,为了设置 React app,我们将使用create-react-app包为我们自动生成一个前端构建管道。但是首先,确保您已经安装了 Node.js 和 npm ,以便使用npx,这是一个随 npm 5.2+ 一起提供的包运行工具。

在我们进入设置我们的环境之前,以下是本章中需要安装的工具:

  • React.js :一个用于构建用户界面的 JavaScript 框架
  • 可拖动:一个拖放库,使得可以移动 HTML 元素
  • 反应-图表-js :一个用于chart组件的反应库
  • 反应-表格-v6 :显示表格的反应库

以下是上述工具的一些替代方案:

  • Vue.js :一个用于构建用户界面的 JavaScript 库
  • rechart.js :基于 React.js 构建的可组合图表库
  • 材料表:基于材料-界面反应的数据表

为了创建一个反应应用管道,我们使用npx调用create-react-app,然后指定我们项目的名称如下:

$ npx create-react-app data-art

该命令将在发起命令的父目录中创建一个名为data-art的目录。这个data-art目录预先填充了 React.js 模板和所有需要的包。

以下是data-art文件夹的结构:

Figure 8.1 – React.js directory structure

图 8.1–react . js 目录结构

安装完成后,我们始终可以使用以下命令启动 app(假设您不在终端的data-art目录中):

$ cd data-art
$ yarn start

以下命令将启动应用服务器,并输出终端中运行应用的服务器端口:

Figure 8.2 – yarn start output

图 8.2–纱线开始输出

图 8.1 所示,app 在http://localhost:3000服务。如果一切正常,那么一旦服务器启动,它就会自动打开网络浏览器显示 React 应用。

对于这个 app 的开发,我们不会花更多的时间在造型上,但我们会让未来的造型更容易集成,也能快速原型化;我们将利用tailwindcss

为了让 Tailwind 与 React.js 应用一起工作,我们需要做一些额外的配置。

让我们通过npm安装 Tailwind 及其对等依赖项,如tailwindcss文档所示:https://tailwindcss.com/docs/guides/create-react-app:

npm install -D tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9

一旦安装完成,我们将继续安装craco模块,这允许我们覆盖postcss配置,如下所示:

npm install @craco/craco

安装craco后,我们可以继续配置如何构建、启动和测试 React app。这将通过将package.json"start""build""test"的命令更改为以下命令来完成:

{
  "start": "craco start",
  "build": "craco build",
  "test": "craco test",
}

根据之前所做的更改,让我们创建一个配置文件,使craco能够在构建 React 应用时始终注入tailwindcssautoprefixer,如下面的代码所示:

// craco.config.js
module.exports = {
  style: {
    postcss: {
      plugins: [
        require('tailwindcss'),
        require('autoprefixer'),
      ],
    },
  },
}

让我们配置tailwindcss本身。有了这个配置,我们可以告诉tailwindcss去掉生产中不用的样式,可以添加自定义主题,也可以添加tailwindcss包中没有的自定义颜色、字体、宽度、高度,如下:

//tailwind.config.js
module.exports = {
  purge: ["./src/**/*.{js,jsx,ts,tsx}", "./public/index.html"],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};

配置好顺风后,我们将在src目录下编辑css文件index.css。我们将在文件中添加以下内容:

/* ./src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

我们已经完成了配置;我们现在可以在index.js中导入index.css:

//index.js
. . . . . .
import "./index.css
. . . . . .

注意在App.js中,我们仍然有create-react-app包附带的默认代码;让我们编辑代码。下面是初始代码:

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

我们通过编辑 HTML 并用应用名称替换来编辑App评论中的 HTML 代码:

function App() {
  return (
    <div className="">
      Data-Art
    </div>
  );
}

通过用前面的代码更新App.js并保存,您应该可以直接在浏览器中看到正在进行的更改,如下图所示:

Figure 8.3 – React app

图 8.3–反应应用

让我们测试一下我们的tailwindcss配置,确保设置正确。我们将通过在前面的代码中添加一些样式来做到这一点,如下所示:

function App() {
  return (
    <div className="max-w-2xl border mx-auto text-3xl mt-60 text-center">
      Data-Art
    </div>
  );
}

css样式在名为classNamediv属性中声明。首先,我们设置最大宽度和边框,然后沿着 x 轴创建边距(左右边距为auto),将字体大小声明为text-3xl,将页边距顶部设置为60,然后将文本在div实例内居中。

根据样式,我们应该会看到以下输出:

Figure 8.4 – Centering the div and text

图 8.4–将 div 和文本居中

代码库已经设置好了,我们准备好实现我们的无代码环境。

在这一节中,我们看到了如何为我们的应用设置一个 React 环境。我们也看到了如何为我们的应用配置tailwindcss。在下一节中,我们将学习如何构建和设计应用。

构建和设计应用

React.js 有一些 app 设计的核心理念,大部分是把 UI 分解成一个组件层次,也有一个思路是识别你的状态应该生活在哪里。

在这一节中,我们将看到如何用 React.js 设计我们的无代码应用的结构,并考虑应用设计的 React 哲学。有了这个原则,我们会发现在 React 中实现一个基本的 UI 很容易。

首先,让我们了解什么是无代码环境,以及我们想要用它实现什么。无代码环境用于使数据处理和分析更容易,只需点击几个按钮。

我们将创建一个平台,用户可以在其中上传他们的数据,执行分析,并使用代码进行操作,例如:

  • 数据帧到数据帧的操作,如concat
  • cummax``cumsum等算术运算
  • 查询以按列值筛选出数据帧
  • 描述数据帧

我们希望能够在不实际编码的情况下完成所有这些工作,一切都将在浏览器中完成。我们还希望能够使用条形图、折线图和饼图,通过数据可视化从数据中获取见解。下图显示了应用设计的草图:

Figure 8.5 – App structure and design sketch

图 8.5–应用结构和设计草图

图 8.5 展示了 app 的结构和设计。 app 分为三个主要组件,如下所示:

  • Navbar组件,包含文件上传、条形图、折线图和DataFrame操作选择字段
  • 主体包含Data Table部件和chart部件
  • SideBar,包含图表和DataFrame操作的侧面

应用工作流程可以描述如下:

  1. 首先,上传一个数据文件(csv)。
  2. 通过上传文件,创建第一个Data Table。这是一个包含数据框显示的组件。
  3. 要执行任何操作,如DataFrame操作或图表操作,选择数据表,这样我们就可以识别正确的表来执行操作。
  4. 对于图表操作,单击条形图、折线图或饼图。该点击事件激活图表操作的Side Plane
  5. 如果选择了DataFrame操作,则DataFrame操作的Side Plane被激活。
  6. 当您填写完Side Plane中的必要字段后,会创建一个新的图表组件和Data Table组件。

下图描述了整个工作流程:

Figure 8.6 – App workflow

图 8.6–应用工作流程

工作流显示了每个组件如何相互响应。比如不上传文件,主体和Side Plane就看不见了。即使上传了文件,Side Plane仍保持隐藏状态,只有在特定数据表上执行数据框或图表操作时才会出现。

由此可见,我们需要创建一个状态来管理每当上传文件时主体的激活,还需要创建一个状态来管理在数据表上进行操作时Side Plane是如何激活的。另外,注意Side Plane包含两个操作,我们必须根据选择的操作类型显示这些操作的字段。

如果选择的是图表操作,Side Plane需要显示所选图表的必要字段,无论是条形图、折线图还是饼图,如果选择的是DataFrame操作,Side Plane需要显示数据框操作字段,如下图所示:

Figure 8.7 – Side plane operation fields

图 8.7–侧面操作区域

图 8.7 可以看出,数据表和chart组件有一个 xy 符号。这表明我们可以有多个数据表或图表组件,并且每个组件都是可拖动的。因此,我们需要创建一个状态来管理Data Table组件和chart组件的列表。每个组件都有一个状态,这使得创建、更新和删除组件成为可能。

如应用工作流程所述,Side Plane操作需要数据进行可视化和DataFrame操作,这些数据是通过点击我们想要的Data Table来获取的。每个Data Table存储自己的数据帧对象(我们将在实现这些步骤时深入研究这一点)。因此,每当单击一个数据表时,就会获得它在数据表状态中的索引,并将其传递到数据表状态旁边的边平面中,该边平面指示要处理哪个数据表。

此外,为了侧面了解操作需要哪种图表类型(条形图、折线图或饼图)或要执行哪种类型的DataFrame操作,我们创建了一个状态来管理当前选择的是哪种类型的图表或DataFrame

总之,所需的状态集合描述如下:

  • 管理DataTable列表的状态
  • 管理图表列表的状态
  • 状态显示管理SidePlane的可见性
  • 管理当前DataTable索引的状态
  • 管理所选图表类型的状态
  • 选择管理当前DataFrame操作的状态

这里创建的状态没有得到很好的优化。可以管理创建的状态数量;例如,管理Side Plane可见性的相同状态也可以用于管理所选图表的类型。

由于我们将使用一个或两个以上的状态,并且一些状态与另一个状态交互,我们可以使用useReducer(一个反应钩子)来管理状态交互,但是我们希望在不增加额外开销的情况下使这变得简单。

在这一节中,我们讨论了应用的设计和结构。我们还设计了应用工作流程,并讨论了要为应用创建的不同状态。在下一节中,我们将讨论应用布局和DataTable组件。我们将看到如何创建数据表组件以及如何管理状态。我们还将研究用 Danfo.js 在浏览器中上传文件。

应用布局和数据表组件

在本节中,我们将看到如何基于上一节中讨论的设计和工作流程来布局应用。另外,我们将实现DataTable组件,负责DataFrame表的显示。我们还将实现DataTables组件,负责显示不同的DataTable组件。

我们已经看到了应用的草图,也看到了该应用的基本工作流程。我们将通过首先构建应用的基本布局,然后实现数据表组件来开始实现步骤。

有了tailwindcss,布局 app 相当容易。让我们创建一个名为App.js的文件,并输入以下代码:

function App() {
  return (
    <div className="max-w-full mx-auto border-2 mt-10">
      <div className="flex flex-col">
        <div className="border-2 mb-10 flex flex-row">
          Nav
        </div>
        <div className="flex flex-row justify-between border-2">
          <div className="border-2 w-full">
            <div>
              Main Body
            </div>

          </div>
          <div className="border-2 w-1/3">
            Side Plane
          </div>
        </div>
      </div>

    </div>
  );
}

前面的代码片段用一个flex框创建了应用的布局。这个布局展示了应用的基本组件,分别是NavMain BodySide Plane

注意

教程中使用的css实例就不解释了。我们的主要重点是构建应用的功能。

如果一切顺利,我们应该得到如下输出:

Figure 8.8 – App layout

图 8.8–应用布局

我们已经布置好了应用。让我们继续实现DataTable组件来显示所有DataFrame操作的结果。

实现数据表组件

一个DataTable组件是负责数据表的显示。对于每个数据框操作,我们生成一个新数据表,显示操作结果,如下图所示:

Figure 8.9 – Data table

图 8.9–数据表

为了显示表格,我们将使用名为react-table-v6的 React 包,由于我们希望DataTable组件可以在页面上拖动,因此有一个名为react-draggable的包,这使得实现该功能更加容易。

注意

DataTable的代码可以从这里拉出来:https://github . com/PacktPublishing/Building-Data-Driven-Applications-with-danfo . js/blob/main/chapter 08/src/components/datatable . js

我们需要使用yarn将这些包添加到我们的代码库中:

yarn add react-table-v6 react-draggable

安装好软件包后,让我们按照以下步骤在src/component/DataTable.js文件中创建一个DataTable组件:

  1. 我们进口必要的包装:

    js import React from "react"; import ReactTable from 'react-table-v6' import Draggable from 'react-draggable'; import 'react-table-v6/react-table.css'

  2. We create the DataTable component:

    js export default function DataTable({ columns, values, setCompIndex, index }) {   // DataTable component code here }

    DataTable组件采用以下props值:

    • columns:数据表列名。
    • values:各列的数据表值。
    • setCompIndex:这是一个状态函数,用于管理当前选中的数据表。
    • index:这是当前表的索引。
    • For the react-table component, we need to reshape the column and the values to fit the desired input for the react-table component.

    让我们重塑要传递到react-table的列值:

    js const dataColumns = columns.map((val, index) => {     return { Header: val,       accessor: val,       Cell: (props) => (         <div className={val || ''}>           <span>{props.value}</span>         </div>       ),       width:         index === 0 && (1280 * 0.8333 - 30) / columns.length < 130           ? 130           : undefined,     }   });

    使用前面的代码,作为表的Header的列名被转换为以下形状:

    js [{   Header: "A",   accessor: "A" },{   Header: "B",   accessor: "B" },{   Header: "C",   accessor: "C" }]

    Header键是表中要显示的列名,accessor是数据中的键。

  3. 我们需要将数据表值转换成react-table需要的格式。以下代码用于转换数据表值:

    js const data = values.map(val =>{     let rows_data = {}     val.forEach((val2, index) => {       let col = columns[index];       rows_data[col] = val2;     })     return rows_data;   })

如前代码所示,我们将数据表值转换成如下数据形式:

[{
  A: 2,
  B: 3,
  C: 5
},{
  A: 1,
  B: 20,
  C: 50
},{
  A: 23,
  B: 43,
  C: 55
}]

最初,values是一个数组的数组,该数组被转换为前面的数据格式,然后被分配给data变量。

在前面的列格式中声明的访问器指向字典中每个键的值。有时,我们可能会有以下格式的嵌套数据:

[{
  dummy: {
    A: 1.0,
    B: 3.0
  },
  dummy2: {
    J: "big",
    k: "small"
  }
}, . . . . ]

对于这种类型的数据格式,我们可以将data列声明为以下格式:

[{
  Header: "A",
  accessor: "dummy.A"
},
{
  Header: "B",
  accessor: "dummy.B"
},
{
  Header: "J",
  accessor: "dummy2.J"
},
{
  Header: "K",
  accessor: "dummy2.K"
}]

对于这个项目,我们不会使用这种嵌套的数据格式,所以不需要深入研究,但是如果你好奇,你可以查看react-table-v6文档。

包括Header在内的列名和表数据现在格式正确,可以传递到react表中。DataTable组件现已更新,包含以下代码:

function DataTable({ columns, values, setCompIndex, index }) {
 . . . . . . . . . . . . . . . . . . . .
const handleSidePlane = ()=>{
    setCompIndex(index)
  }
  return (
    <Draggable >
        <div className="w-1/2" onClick={()=> handleSidePlane()}>
        <ReactTable
          data={data}
          columns={dataColumns}
          getTheadThProps={() => {
            return { style: { wordWrap: 'break-word', whiteSpace: 'initial' } }
          }}
          showPageJump={true}
          showPagination={true}
          defaultPageSize={10}
          showPageSizeOptions={true}
          minRows={10}
        />
    </div>
    </Draggable>
  )  
}

ReactTable组件包裹在Draggable组件中,使DataTable组件可拖动。在ReactTable组件中,我们设置了一些分页字段,比如将默认页面设置为10

回想一下,在设计我们提到的应用的工作流程时,是如何在点击时跟踪一个Data Table的 ID 的。handleSide Plane功能用于拨打setCompIndex电话。setCompIndex用于更新compIndex状态,存储所选Data Table的索引。

注意

DataTables的代码在这里可以找到:https://github . com/PacktPublishing/Building-Data-Driven-Applications-with-danfo . js/blob/main/chapter 08/src/components/datatables . js

每个操作会生成多个数据表,因此我们需要管理这个Data Table的显示。我们将创建一个组件来管理所有生成的Data Tables的显示;因此,我们将在组件目录中创建一个文件,并将其命名为Data Tables,其中包含以下代码:

import React from 'react'
import DataTable from './DataTable'
export default function DataTables({datacomp, setCompIndex,}) {
  return (
    <div>
      {datacomp.map((val,index) => {
        return( 
                <>
                <DataTable 
                  key={index} 
                  columns={val.columns} 
                  values={val.values} 
                  setCompIndex={setCompIndex}
                  index={index}                />
                </>
                )
      })}
    </div>
  )
}

该组件循环通过datacomp状态,并将每个道具传递到DataTable组件。

在下一小节中,我们将继续并初始化不同的状态,还将展示如何上传 CSV 并获取我们的数据。

文件上传和状态管理

从 app 设计中,我们看到任何操作的发生,都需要先上传一个文件。通过上传文件,我们创建了将被DataTablechart组件使用的DataFrame

注意

在本章中,App.js中的代码是基于新组件的实现而逐渐更新的。但是App.js的最终代码可以在这里找到:https://github . com/PacktPublishing/Building-Data-Driven-Applications-with-danfo . js/blob/main/chapter 08/src/app . js

我们将通过以下步骤更新App.js中的代码,以包含Data组件状态、文件上传和状态更新:

  1. 我们进口React和一个名为useState的反应钩:

    js import React, { useState } from 'react';

  2. 我们导入read_csv方法,用于读取上传的 CSV 文件:

    js import { read_csv } from 'danfojs/src/io/reader' // step 2

  3. 我们创建一个状态来存储生成的每个DataTable组件的数据列表:

    js const [dataComp, setDataComp] = useState([])

  4. We then create a function to manage file upload and read the uploaded file into a DataFrame:

    ```js const changeHandler = function (event) {     const content = event.target.files[0]     const url = URL.createObjectURL(content)

    read_csv(url).then(df => {       const columns = df.columns       const values = df.values       setDataComp(prev => {         let new_data = prev.slice()         let key = new_data.length + 1         let dict = {           columns: columns,           values: values,           df: df,           keys: "df" + key         }         new_data.push(dict)         return new_data       })

    }).catch((error) => {       console.log(error)     })   } ```

    在前面的代码中,我们用URL.createObjectURL从上传的文件中生成一个 blob URL。这样做是因为Danfo.js中的read_csv代码只取自 CSV 文件的本地路径、CSV 文件的 HTTP 网址和 CSV 文件的 blob 网址。

    生成的网址是,然后传入read_csv函数。由于read_csv是一个异步函数,我们需要等待承诺被解析,然后通过then方法从承诺中收集返回值。解决承诺的返回值是DataFrame

    通过read_csv,CSV 数据转换为DataFrame,然后更新DataComponent状态。使用setDataComp状态功能,我们创建了一个包含以下键的对象:

    a) columns:存储 CSV 文件的表头(列名)

    b) values:存储 CSV 数据点,即DataFrame

    c) df:存储生成的DataFrame

    d) keys:为每个数据组件data生成一个密钥

    在将数据帧本身保存到每个组件的状态数据中之前,需要做出一个决定。因为我们存储了列名和DataFrame值,所以它看起来是多余的。

    但是我们最终选择存储它的原因是,每次我们需要执行DataFrame操作时,总是从列和值创建一个DataFrame会在计算上很昂贵。

    此外,columnsvalues被存储起来,以便在我们想要从react-table组件生成一个表时,可以方便地访问。但是,这仍然感觉是多余的,作为个人练习(在本节末尾列出的待办事项列表中),你可以继续清理它。

  5. We print out the output of the dataComp state in the browser console once the state is updated:

    js if (dataComp.length) { //step 8     console.log("dataComp column", dataComp[0].columns)     console.log("dataComp values", dataComp[0].values)     console.log("dataComp dataFame", dataComp[0].df)   }

    以下截图显示了应用的更新用户界面:

Figure 8.10 – Updated UI for file upload

图 8.10–文件上传的更新用户界面

上传文件后,我们应该会在浏览器控制台中看到以下输出:

Figure 8.11 – dataComp state output

图 8.11–数据组件状态输出

我们已经为每个DataTable组件设置了文件上传和状态管理。让我们将创建的DataTables组件集成到应用中。

将数据表组件集成到 App.js 中

App.js将通过以下步骤更新:

  1. 我们导入DataTables组件并创建compIndex状态,这使我们能够存储我们想要在时刻处理的DataTable组件的索引:

    js . . . . . . . . . . . . import DataTables from './components/DataTables'; function App() {    . . . . . . . . . .   const [compIndex, setCompIndex] = useState()   . . . . . . . . . . }

  2. We then add the DataTables component to the App component:

    js <div>     {(dataComp.length > 0) &&         <DataTables             datacomp={dataComp}             setCompIndex={setCompIndex}         />      } </div>

    为了启用DataTable组件可见性,我们检查dataComp状态是否为空。上传文件前,如果dataComp状态为空,DataTable组件将不可见。一旦文件被更新,DataTable组件变得可见,因为dataComp状态不再为空。

    上传文件后,前面的代码应该会给出以下输出:

Figure 8.12 – Display of the DataTable component on file upload

图 8.12–文件上传时数据表组件的显示

在这一节中,我们讨论了文件上传和DataTable的创建和管理,并看到了如何管理状态。在下一节中,我们将实现不同的DataFrame操作组件,并为DataFrame操作实现Side Plane

创建不同的数据框操作组件

在本节中,我们将创建不同的DataFrame操作组件,并为DataFrame操作组件实现Side Plane。Danfo.js 包含很多DataFrame操作。如果我们要为每个组件设计一个组件,这将是非常紧张和多余的。

为了防止为每个DataFrame方法创建组件,我们根据每个DataFrame操作的(关键字)参数,也就是说,根据传递给它们的变量,对它们进行分组。例如,有一些DataFrame方法只接受操作轴,因此我们可以将这些类型的方法组合在一起。

以下是要创建的DataFrame操作组件的列表以及在这些组件下分组的DataFrame方法:

  • 算术组件:这个包含DataFrame方法,它的自变量只有运算轴,可以是1也可以是0。对DataFrame进行算术运算的方法有minmaxsumstdvarsumcumsumcummaxcummin
  • Df2df 组件:包含两个DataFrame组件之间的操作,比如一个DataFrame和一个序列、一个值或一个DataFrame之间的逻辑操作。进行这些操作的方法是concatltgteltegtneq
  • 查询组件:这是DataFrame查询的组件。
  • 描述组件:这是描述DataFrame统计的组件。

我们将按照以下顺序从最不复杂的开始查看这些组件的实现:DescribequeryDf2dfArithmetic

实现描述组件

在本节中,我们将实现Describe组件,并集成Side Plane组件。

src/Components/目录下,我们再创建一个名为Side Planes的文件夹。该文件夹将包含DataFrame操作的所有组件。

Side Planes/文件夹中,让我们创建一个名为Describe.js".js" 文件,并按照以下步骤进行更新:

  1. 我们创建Describe功能组件,采用dataComp状态和setDataComp状态功能,用生成的DataFrame :

    js export default function Describe({ dataComp, setDataComp}) { }

    更新dataComp状态 2. We create a button named Describe:

    js return (     <div>       <button onClick={()=> describe()} className="bg-blue-700 text-white rounded-sm p-2">Describe</button>     </div> )

    Describe组件有一个按钮接口,因为它不接受任何参数。该按钮有一个onClick事件,只要点击该按钮就会触发Describe功能。

  2. We then implement the describe() function, which is triggered anytime the describe button is clicked:

    ```js const describe = ()=> {     const df = dataComp.df.describe()     let column = df.columns.slice()     column.splice(0,0, "index")     const values = df.values     const indexes = df.index

    const new_values = values.map((val, index)=> {       let new_val = val.slice()       new_val.splice(0,0, indexes[index])        return new_val     }) . . . . . . . . } ```

    我们从dataComp状态获取包含DataFramedf键,然后调用describe方法。

    从由describe操作生成的DataFrame中,我们获得列,并向列名列表添加索引。索引添加在列表的开头;这样做是因为需要从describe 方法生成的索引来捕获数据中的每一行。

    接下来,我们获得DataFrame值,循环通过它,并将索引值添加到获得的DataFrame值。

  3. 我们用新生成的列、值和DataFrame :

    js setDataComp(prev => { // step 7     let new_data = prev.slice()     let dict = {       columns: column,       values: new_values,       df: df     }     new_data.push(dict)     return new_data })

    更新dataComp状态

要查看该组件的运行情况,我们需要执行DataFrame操作选择字段,如图 8.5 中的App设计草图所示。这个DataFrame操作选择区域使我们能够选择在Side Plane显示哪个DataFrame操作组件。

为此,我们需要在用于文件上传的输入字段旁边添加Navbar组件中用于DataFrame操作的字段。此外,我们需要为每个Side Plane中显示的DataFrame操作组件实现条件渲染。

为描述组件设置侧面

Side Planes/文件夹中,我们创建一个名为Side Plane.js的文件,并输入以下代码:

import React from 'react'
import Describe from './Describe'
export default function SidePlanes({dataComp, 
  dataComps,
  setDataComp,
  df_index,
  dfOpsType}) {

    if(dfOpsType === "Arithemtic") {
      return <div> Arithmetic </div>
    }
    else if(dfOpsType === "Describe") {
      return <Describe 
          dataComp={dataComp}
          setDataComp={setDataComp}
      />
    }
    else if(dfOpsType === "Df2df") {
      return <div> Df2df </div>
    } 
    else if(dfOpsType === "Query") {
      return <div> Query </div>
    }
  return (
    <div>
      No Plane
    </div>
  )
}

在前面的代码中,我们创建了一个Side Plane组件。此组件包含基于所选数据操作类型的条件呈现。选择的DataFrame操作由dfOpsType州管理。

Side Plane进入dataComp状态,可以是dataComps状态下存储的任何数据。一些DataFrame操作将需要所选择的dataComp状态以及整个状态,即dataComps状态来进行操作。

Side Plane组件中,我们将检查dfOpsType以找出通过的操作类型和要在侧面渲染的界面。

在我们将Side Plane集成到App.js之前,让我们在Side Planes/文件夹中创建一个index.js文件。这样,我们就可以定义要导入的组件。由于我们使用的是来自Side Plane组件的条件渲染,我们只需要导出index.js实例中的Side Plane组件,如以下代码所示:

import SidePlane from './SidePlane'
export { SidePlane }

前面的代码使我们能够在App.js中导入Side Plane

将 SidePlane 集成到 App.js 中

Side Plane被创造。让我们将其集成到App.js中,并将DataFrame操作的 HTML select字段添加到App.js,中,如下代码所示:

  1. 我们导入SidePlane组件:

    js import { SidePlane } from './components/SidePlanes'

  2. We update the App component functionality with the following code:

    js function App() {   . . . . . . . .   const [dfOpsType, setDfOpsType] = useState() // step 2   const [showSidePlane, setSidePlane] = useState(false) //step 3   . . . . . . . . .   const dataFrameOps = ["Arithemtic", "Describe", "Df2df", "Query"] // step 4   const handleDfops = (e) => { //step 6     const value = e.target.value     setDfOpsType(value)     setSidePlane("datatable")   } . . . . . . . . . . . . . . }

    在前面的代码中,我们创建了dfOpsType状态来存储当前选择的DataFrame操作类型。

    showSidePlane也是创建来管理SidePlane的可见性。此外,还创建了一系列DataFrame操作。然后我们创建一个函数来处理每当点击DataFrame操作时更新dfOpsTypeshowSidePlane状态。

  3. We then add the SidePlane component:

    js <div className="border-2 w-1/3">      {showSidePlane           &&           (            showSidePlane === "datatable" ?               <div className="border-2 w-1/3">                 <SidePlane                   dataComp={dataComp[compIndex]}                   dataComps={dataComp}                   df_index={compIndex}                   setDataComp={setDataComp}                   dfOpsType={dfOpsType}                  />               </div> :               <div className="border-2 w-1/3">                     Chart Plane               </div>          )     } </div>

    在前面的代码中,我们通过首先检查SidePlane状态不是来显示SidePlane,然后检查要显示的SidePlane类型。由于我们只在DataFrame操作列表中实现了Describe组件,让我们上传一个文件,然后执行一个DataFrame操作。以下截图显示了在DataTable上执行Describe操作的结果:

Figure 8.13 – Describe operation on DataTable

图 8.13–描述数据表上的操作

在前面的截图中,左上角的数据表是上传文件时生成的,右下角的DataFrameDescribe操作的结果。

在本节中,我们看到了如何实现Describe组件以及如何管理Side Plane可见性。在下一节中,我们将在DataFrame中实现Query方法的Query组件。

实现查询组件

在本节中,我们将为DataFrame查询方法创建一个组件。该组件将有助于根据Data Table的列值过滤DataFrame

注意

一个Query组件的代码可以在这里找到:https://github . com/PacktPublishing/Building-Data-Driven-Applications-with-danfo . js/blob/main/chapter 08/src/components/side planes/query . js

让我们在名为Query.jscomponents/Side Planes/文件夹中创建一个文件,并按照以下步骤更新它:

  1. We create the Query component:

    ```js import React, { useRef } from 'react'

    export default function Query({ dataComp, setDataComp}) {   // step 1   const columnRef = useRef()   const logicRef = useRef()   const valuesRef = useRef() . . . . . . . . . . . . } ```

    我们创建了一个useRef钩子变量,它使我们能够获得输入到以下输入字段的当前值:列字段(取列名到查询)、逻辑字段(取要用于查询的逻辑值)和值字段(取要用于查询所选列的值)。

  2. We then update the Query component with the following code:

    js   const columns = dataComp.columns   const logics = [">", "<", "<=", ">=", "==", "!="]

    在前面的代码中,我们获得了当前Data TableDataFrame中可用的列名。此列名将用于填充选择字段,用户可以选择要查询的列。

    我们还创建了一个符号列表,用来描述我们想要执行的逻辑操作的类型。该符号还将用于填充选择字段,用户可以在其中选择用于查询的逻辑操作。

  3. 创建query功能。只要点击查询按钮,就会触发该功能进行查询操作:

    ```js const query = ()=>{     const qColumn = columnRef.current.value     const qLogic = logicRef.current.value     const qValue = valuesRef.current.value

    const  df = dataComp.df.query({column: qColumn, is: qLogic, to: qValue})     setDataComp(prev => {       let new_data = prev.slice()       let dict = {         columns: df.columns,         values: df.values,         df: df       }       new_data.push(dict)       return new_data     })   } ```

每当query 功能被触发时,我们获得每个输入字段(选择字段)的值。例如,为了获得列字段的值,我们使用columnRef.current.value。同样的事情也适用于获取另一个字段中的值。

我们还调用了属于当前dataComp状态的DataFrame的查询方法。从每个输入字段获得的值被传递到查询方法中以执行操作。

使用setDataComp状态功能更新dataComps状态。通过更新dataComps状态,创建包含query方法结果的新DataComp状态。

实现查询组件接口

我们已经看到了Query组件的后端,所以现在让我们为它构建一个接口。让我们按照以下步骤更新Query.js中的上述代码:

  1. For the query UI, we create a form containing three different input fields. First, we create the input field for the column field:

    js <div>   <span className="mr-2">Column</span>     <select ref={columnRef} className="border">       {          columns.map((column, index)=> {           return <option value={column}>{column}</option>          })       }     </select> </div>

    对于列字段,我们循环遍历列数组,为DataFrame中的列列表创建 HTML 选择字段选项。我们还包括columnRef来跟踪选定的列名。

  2. We then create the logic input field:

    js <div>   <span className="mr-2">is</span>   <select ref={logicRef} className="border">      {        logics.map((logic, index)=> {          return <option value={logic}>{logic}</option>        })      }   </select> </div>

    我们循环通过logic数组,并用逻辑运算符填充 HTML 选择字段。此外,将logicRef添加到 HTML 选择字段中,以获得选定的逻辑运算符。

  3. 然后,我们为查询值

    js <div>   <span className="mr-2">to</span>     <input ref={valuesRef} placeholder="value" className="border"/> </div>

    创建input字段 4. We create a button class name to make a call to the query function:

    js <button onClick={()=>query()} className="btn btn-default dq-btn-add">Query</button>

    为了可视化主应用中的query组件,让我们更新SidePlane.js中的SidePlane组件:

    js Previous code: . . . . . . . . else if(dfOpsType === "Query") {       return <div> Query </div>     } Updated code: else if(dfOpsType === "Query") {       return <Query             dataComp={dataComp}             setDataComp={setDataComp}       />     }

前面的代码更新了Side Plane以包含Query组件。如果我们对上传的文件执行查询操作,应该会得到以下结果:

Figure 8.14 – Query operation run on column C, checking whether its value is greater than 20

图 8.14–对列 C 运行查询操作,检查其值是否大于 20

在本节中,我们创建了一个query组件。在下一节中,我们将研究为操作创建一个组件,包括DataFrame-到- DataFrame操作、序列和标量值。

实现 Df2df 组件

在本节中,我们将实现一个组件,用于在一个DataFrame和另一个DataFrameSeriesScalar值之间执行操作。

注意

Df2df组件的代码可在此处获得:https://github . com/packt publishing/Building-Data-Driven-Applications-with-danfo . js/blob/main/chapter 08/src/components/side planes/df2df . js

Danfo.js 中有不同的组件执行DataFrameSeries以及DataFrameScalar值之间的操作。为了避免必须为这些方法创建一个组件,我们可以将它们组合在一起形成一个组件。

我们计划分组的DataFrame方法列表如下:

  • 小于(df.lt)
  • 大于(df.gt)
  • 不等于(df.ne)
  • 等于(df.eq)
  • 大于或等于(df.ge)
  • 加法(df.add)
  • 减法(df.sub)
  • 乘法(df.mul
  • 除法(df.div)
  • 功率(df.pow)

在前面的方法列表中,一个常见的属性是它们都接受相同类型的参数,即值(可以是DataFrameSeriesscalar值)和要执行操作的轴。

如果我们看一下DataFrame concat方法,它也接受与前面列表中的方法类似的相同模式的参数。唯一不同的是,对于concat方法来说,df_list参数是一组DataFrames

让我们在名为Df2df.jsSide Planes/文件夹中创建一个文件。在本文件中,我们将通过以下步骤实现Df2df组件:

  1. First, we import concat from Danfo.js, and then create the Df2df component:

    ```js import React, { useRef } from 'react' import { concat } from 'danfojs/src/core/concat'

    export default function Df2df({dataComp, dataComps,df_index, setDataComp}) {   const dfRef = useRef()   const inpRef = useRef()   const axisRef = useRef()   const opsRef = useRef()

    const allOps = [     "lt", "ge", "ne",     "eq", "gt", "add",     "sub", "mul", "div",     "pow", "concat"   ] . . . . . . . . .  . . . . } ```

    我们为每个输入字段创建了一个引用变量。对于Df2df操作,我们有四个输入字段(DataFrame选择字段、scalar值输入字段、axis字段和operation类型字段)。

    operation类型字段包含Df2df组件中所有可用操作的列表。这将是一个选择字段,因此用户可以选择任何要处理的操作。

    我们还创建了一个由Df2df 组件提供的所有操作的allOps列表。

  2. We also need to create a function to perform the Df2df operation whenever the submit button is clicked:

    js const df2df = () => {     // step 4     let dfIndex = dfRef.current.value     let inp = parseInt(inpRef.current.value)     let axis = parseInt(axisRef.current.value)     let ops = opsRef.current.value . . . . . . . . . . . . . . }

    我们从属于每个输入字段的所有参考变量中获得值。

  3. We update the df2df function with the following code:

    ```js   if( ops != "concat") {       let value = dfIndex === "None" ? inp : dataComps[dfIndex].df       let df = dataComp.df

    let rslt = eval('df.${ops}(value, axis=${axis})') // step 6

    setDataComp(prev => {         let new_data = prev.slice()         let key = new_data.length +1         let dict = {           columns: rslt.columns,           values: rslt.values,           df: rslt,           keys: "df" + key         }         new_data.push(dict)         return new_data       })     } ```

    我们检查选择的操作不是concat操作。这样做是因为concat操作采用的是DataFrames列表,而不仅仅是DataFrameSeries

    我们利用eval功能防止写入多个if条件来检查调用哪个DataFrame操作。

  4. 我们执行concat操作的条件。我们也打电话到DataFrame中的concat方法:

    ```js . . . . . . . . . else { // step 7       let df2 = dataComps[dfIndex].df       let df1 = dataComp.df       let rslt = concat({ df_list: [df1, df2], axis: axis })

    let column = rslt.columns.slice()       column.splice(0,0,"index")       let rsltValues = rslt.values.map((val, index) => {         let newVal = val.slice()         newVal.splice(0,0, rslt.index[index])         return newVal       })      . . . . . . . . . . . } ```

前面的步骤展示了Df2df组件的后端实现。

实现 Df2df 组件接口

让我们按照以下步骤更新界面的代码:

  1. For the UI, we need to create a form containing four input fields. First, we create an input field to select the type of DataFrame operation we want:

    js <div>   <span className="mr-2"> Operations</span>    <select ref={opsRef}>     {       allOps.map((val,index) => {        return <option value={val} key={index}>{val}</option>        })      }    </select> </div>

    我们循环通过allops数组创建一个input字段来选择不同类型的DataFrame操作。

  2. We then create an input field to select the DataFrame we want to perform the operation selected on:

    js <div>   <span className="mr-2"> DataFrames</span>    <select ref={dfRef}>       <option key={-1}>None</option>         {           dataComps.map((val,index) => {             if( df_index != index) {               return <option value={index} key={index}>{'df${index}'}</option>             }          })         }    </select> </div>

    我们还循环通过dataComps状态,以获得除了我们正在执行操作的dataComp状态之外的所有dataComp状态。

  3. 然后我们创建一个input字段来输入我们的值;在这种情况下,我们在DataFrameScalar值之间执行操作:

    js <div>   <span>input a value</span>   <input ref={inpRef} className="border" /> </div>

  4. 我们创建一个input字段来选择操作轴:

    js <div>   <span>axis</span>   <select ref={axisRef} className="border">     {       [0,1].map((val, index) => {         return <option value={val} key={index}>{val}</option>        })     } </select> </div>

  5. We then create a button that triggers the df2df function to perform a Df2df operation based on the input fields:

    js <button onClick={()=>df2df()} className="bg-blue-500 p-2 text-white rounded-sm">generate Dataframe</button>

    在前面的步骤中,我们为组件创建了用户界面。

    让我们更新SidePlane组件以包含 Df2df 组件:

    ```js import Df2df from './Df2df' export default function SidePlanes({dataComp,   dataComps,   setDataComp,   df_index,   dfOpsType}) {     . . . . . . . .     else if(dfOpsType === "Df2df") {       return     }    . . . . . . . . .

    } ```

    前面的代码将Df2df组件添加到SidePlane组件中,将所需的道具传递到Df2df组件中。下面的截图显示了两个内容相同的 CSV 文件的上传:

Figure 8.15 – Uploading CSV files with the same content

图 8.15–上传内容相同的 CSV 文件

下图显示了在选定的Data Table上执行Df2df操作(具体为concat操作)的输出:

Figure 8.16 – Performing a concat operation on the Data Table

图 8.16–在数据表上执行连接操作

在本节中,我们创建了Df2df组件,用于在两个DataFrames之间以及一个DataFrame和一个Series / Scalar值之间执行操作。

在下一节中,我们将实现最后一个DataFrame组件,这是用于DataFrame算术运算的arithmetic组件。

实现算术组件

我们将实现arithmetic组件来执行Danfo.js中提供的一些算术运算。

注意

Arithmetic组件的代码可在此处获得:https://github . com/packt publishing/Building-Data-Driven-Applications-with-danfo . js/blob/main/chapter 08/src/components/side planes/arithemtic . js

让我们在Side Planes/文件夹中创建一个名为Arithmetic.js的文件。以下步骤将用于创建Arithmetic组件:

  1. We create an Arithmetic component:

    ```js import React, { useRef } from 'react' export default function Arithmetic({ dataComp, setDataComp}) {

    const seriesOps = ["median", "min", "max", "std", "var", "count", "sum"]   const dfOps = ["cumsum", "cummax", "cumprod", "cummin"]   const all = ["median", "min", "max", "std", "var", "count", "sum",                "cumsum", "cummax", "cumprod", "cummin"]

    const axisRef = useRef()   const opsRef = useRef() . . . . . . . . . . . . } ```

    我们创建不同的数组来存储不同的操作,例如seriesOps用于串行操作,dfOps用于数据帧操作。我们还创建了一个all数组,将所有这些操作(SeriesDataFrame)存储在一起。

  2. We create a function called arithmetic. This function is used to perform the arithmetic operations:

    ```js const arithemtic = () => {

    let sOps = opsRef.current.value     let axis = axisRef.current.value     if( seriesOps.includes(sOps)) {       let df_comp = dataComp.df       let df = eval('df_comp.${sOps}(axis=${axis})')

    let columns = Array.isArray(df.columns) ? df.columns.slice() : [df.columns]       columns.splice(0,0, "index")       let values = df.values.map((val,index) => {

    return [df.index[index], val]       }) . . . . . . . . . . . } ```

    我们从输入字段opsRef.current.valueaxisRef.current.value中获取值。我们也检查选择的操作是否属于seriesOps。如果是这样,我们执行选定的操作。

  3. We perform a DataFrame operation if the operation does not belong to seriesOps:

    ```js else {

    let df_comp2 = dataComp.df       let df = eval('df_comp2.${sOps}({axis:${axis}})')

    setDataComp(prev => {         let new_data = prev.slice()         let dict = {           columns: df.columns,           values: df.values,           df: df         }         new_data.push(dict)         return new_data       })     } ```

    上述步骤用于创建Arithmetic组件。Arithmetic的用户界面与创建的其他DataFrame操作组件的用户界面相同。

    让我们将arithmetic组件添加到SidePlane组件中:

    ```js import Arithmetic from './Arithmetic' export default function SidePlanes({dataComp,   dataComps,   setDataComp,   df_index,   dfOpsType}) {     . . . . . . . .     if(dfOpsType === "Arithmetic") {       return     }    . . . . . . . . .

    } ```

    前面的代码导入Arithmetic组件,检查dfOpsType组件是否为Arithmetic

    下面的截图显示了在Data Table上执行算术运算的例子:

Figure 8.17 – Arithmetic operation

图 8.17–算术运算

在本节中,我们讨论并实现了不同的DataFrame操作作为一个反应组件。我们能够将一些方法组织成单个组件,以防止为每个操作创建组件。

在下一节中,我们将为不同的可视化实现一个chart组件。

实现图表组件

在这一节中,我们将创建chart组件来显示常见和简单的图表,如条形图、折线图和饼图。然后我们将实现图表Side Plane来启用图表组件变量的设置。

注意

实现的ChartChartPlane组件的代码可在此处获得:https://github . com/PacktPublishing/Building-Data-Driven-Applications-with-danfo . js/blob/main/chapter 08/src/components/chart plane . js

src/components/目录下,我们创建一个名为Chart.js的文件,Chart组件将通过以下步骤实现:

  1. We import our desired plotting component from react-chartjs-2, and then create the Chart component:

    ```js import { Bar as BarChart } from 'react-chartjs-2'; import { Line as LineChart } from "react-chartjs-2"; import { Pie as PieChart} from "react-chartjs-2"; import Draggable from 'react-draggable';

    export default function Chart({labels, dataset,type}) {   let data = {     labels: labels,     datasets: [{       backgroundColor: [       . . . . . . . .         ],       borderColor: [       . . . . . . . .         ],       borderWidth:1,       data: dataset,     }]   }; ```

    在前面的代码中,Chart组件接收以下道具:labelsdatasettypelabels表示列名,dataset表示dataComp值,type表示我们要绘制的图表类型。

    Chart组件中,我们创建了一个名为data的变量,它是一个按照react-chartjs-2要求的方式格式化的对象,用于绘制我们想要的图表。

  2. We create a set of conditional rendering here, as we want to render a specific type of chart, based on the prop type passed into the Chart component:

    js if(type==="BarChart"){     return(       <Draggable>         <div className="max-w-md">          <BarChart data={data} options={options} width="100" height="100" />       </div>       </Draggable>     )   } . . . . . . .

    我们检查要渲染的图表类型。如果是条形图,我们从react-chartjs-2调用BarChart组件,传入必要的道具。BarChart组件被包裹在Draggable组件中,以使chart组件被渲染成可拖动的。上述代码适用于渲染所有其他Chart组件,如LineChartPieChart中的react-chartjs-2

要深入了解react-chartjs-2,您可以查看这里的文档:https://github.com/reactchartjs/react-chartjs-2

实现图表平面组件

我们已经创建了chart组件,所以现在让我们创建图表Side Plane。在components/文件夹中,我们按照以下步骤创建一个名为ChartPlane.js的文件:

  1. We create a ChartPlane component:

    js export default function ChartPlane({setChartComp, dataComp, chartType}) {   const df = dataComp.df   const compCols = dataComp.columns   let x;   let y;   if( compCols[0] === "index") {     x = compCols     y = dataComp.values[0].map((val, index)=> {         if(typeof val != "string") {           return compCols[index]         }     })   } else {     x = df.columns     const dtypes = df.dtypes     y = dtypes.map((val, i)=>{         if(val != "string") {           return x[i]         }     })   }

    在前面的代码中,我们创建了一个接受以下道具的ChartPlane组件:

    a) SetChartComp:功能更新chartComp状态

    b) dataComp:生成图表的当前DataTable组件

    c) chartType:我们要生成的图表类型

    首先,在组件中,我们获得可能的 x 轴变量列表,并将它们存储在x变量中。这些 x 轴变量可以是带有String的列名,也可以是数字dtypes

    因为我们正在相对于 x 轴绘制 y 轴,所以我们的 y 轴(变量y必须是整数。因此,我们检查DataFrame的列不是字符串,如果不是,我们将该列添加到 y 轴变量的列表中。

    注意

    这是灵活的。有时图表可以翻转,使得 y 轴实际上是标签,而 x 轴包含数据。

  2. We create the UI for the ChartPlane component. Depending on how we've designed the UI for other components, the x and y variables are used to create an input field with which the user can select the x-axis label and the y-axis label:

    js <select ref={xRef} className="border">   {      x.map((val, index)=> {       return <option value={val} key={index} >{val}</option>      })    } </select> <select ref={yRef} className="border">   {     y.map((val, index) => {       return <option value={val} key={index}>{val}</option>     })   } </select>

    该 UI 还包含一个按钮,触发名为handleChart的功能,更新chart组件:

    js <button onClick={()=>handleChart()} className="bg-blue-500 p-2 text-white rounded-sm">generate Chart</button>

  3. We create a function called handleChart, which obtains the value of the x-axis and y-axis input fields and uses them to create the respective charts as requested:

    js const handleChart = () => {   const xVal = xRef.current.value   const yVal = yRef.current.value   const labels = xVal === "index" ? df.index : df[xVal].values   const data = yVal === "index" ? df.index : df[yVal].values     setChartComp((prev) => {       const newChart = prev.slice()       const key = newChart.length + 1       const dict = {         labels: labels,         data: data,         key: "chart" + key,         type: chartType       }       newChart.push(dict)       return newChart     })   }

    xValyValx 轴和 y 轴的输入字段值。创建了labelsdata变量,以包含来自xValyVal的相应列的值。标签和数据随后用于更新chartComp状态。

实现 ChartViz 组件

前面的步骤是用来创建图表Side Plane,但是目前我们还看不到更新后的chartComp组件。要查看图表,让我们创建一个组件来管理所有要显示的图表组件。

注意

要实现的ChartViz的代码可以在这里找到:https://github . com/packt publishing/Building-Data-Driven-Applications-with-danfo . js/blob/main/chapter 08/src/components/chartsviz . js

让我们在名为ChartViz.jscomponents/文件夹中创建一个文件。将以下代码添加到文件中:

import React from 'react'
import Chart from './Chart'

export default function ChartsViz({chartComp,setChartComp}) {
  return (
    <div>
      {
        chartComp.map((chart)=> {
          return(
            <>
            <Chart 
              labels={chart.labels}
              dataset={chart.data}
              type={chart.type}
          />
          </>
          )
        })
      }
    </div>
  )
}

在前面的代码中,我们导入我们的chart 组件,然后创建一个包含以下chartCompsetChartComp道具的ChartViz组件。我们遍历chartComp状态,并将每个状态值作为一个道具传递给chart组件。

将 ChartViz 和 ChartPlane 集成到 App.js 中

现在我们已经完成了chart组件的所有必要部分。让我们更新我们的App.js组件,根据激活chart组件,步骤如下:

  1. 我们将ChartVizChartPlane导入App.js :

    js import ChartsViz from './components/ChartsViz' import ChartPlane from './components/ChartPlane'

  2. We need to create some state to manage the type of chart we want, and the chart component:

    js const [chartType, setChartType] = useState() const [chartComp, setChartComp] = useState([]) const charts = ["BarChart", "LineChart", "PieChart"]

    在前面的代码中,我们还创建了一个数组变量来存储我们想要在Navbar中显示的图表列表。

  3. We create a function to update the chartType component and the Side Plane component whenever a chart is created:

    js const handleChart = (e) => { // step 4     const value = e.target.innerHTML     setChartType(value)     setSidePlane("chart")   }

    handleChart功能中,我们获得目标值,这是用户选择的图表类型。该值用于更新chartType组件,并且我们通知Side Plane通过用chart字符串更新showSidePlane状态来显示图表Side Plane

  4. We loop the charts variable in the nav field and display them as buttons:

    js . . . . . . {   charts.map((chart, i) => {     return <button disabled={dataComp.length > 0 ? false : true}     className={classes}     onClick={handleChart}    >       {chart}    </button> }) } . . . . . .

    在前面的代码中,我们遍历charts数组,并为数组中的每个值创建一个按钮。我们通过检查dataComp状态不为空,即是否没有上传文件,来禁用按钮。

  5. We call the ChartViz component and pass in the necessary props:

    js {(chartComp.length > 0) &&     <ChartsViz      chartComp={chartComp}      setChartComp={setChartComp}     /> }

    我们检查chartComp状态不为空。如果不是,我们调用ChartViz组件,然后显示创建的图表。

  6. 然后我们添加ChartPlane组件:

    js <div className="border-2 w-1/3">     <ChartPlane        dataComp={dataComp[compIndex]}        setChartComp={setChartComp}        chartType={chartType}     /> </div>

如果showSide Plane图表是数值图表,ChartPlane组件显示在Side Plane中。

以下截图显示了通过在可用的Data Table上绘制条形图、折线图和饼图来更新图表:

Figure 8.18 – Chart component displayed

图 8.18–显示的图表组件

在本节中,我们实现了ChartComponentChartPlane。我们利用React-chart-js来简化每个图表组件的开发。

总结

在这一章中,我们看到了如何创建一个无代码环境,您可以只上传数据,然后立即开始处理和进行数据分析。我们还看到了如何将 Danfo.js 中的每个DataFrame方法转换成一个 React 组件。这提供了将所有的 Danfo.js 方法转换成 React 组件的能力,因此为 Danfo.js 创建了 React 组件库

此外,我们还看到了如何为应用设计流程,以及如何在 React 中管理状态。即使创建的一些状态是多余的,这也是您贡献和更新应用以使其健壮的机会。如果您可以更新应用,使其可以删除、更新和保存正在进行的每个操作,这将使应用健壮,甚至可以投入生产。

在下一章中,我们将介绍机器学习。这一章将以最简单的形式讲述机器学习背后的基本思想。