八、创建无代码数据分析/处理系统
创建 Danfo.js 的主要目的之一是在浏览器中轻松启用数据处理。这提供了将数据分析和数据处理无缝集成到网络应用中的能力。除了能够将数据处理添加到网络应用之外,我们还有工具可以让数据处理和分析看起来更像设计师使用 Photoshop 和 Figma 时所做的事情;他们如何通过点击在画布上混合笔触,或者他们如何通过在画布上放置画布,通过拖放和一些按钮点击来操作图像。
有了 Danfo.js,我们可以轻松地启用这样一个环境(使用工具,如 React.js 和 Vue.js ),在这个环境中,数据科学家变成了艺术家,只需点击几下按钮就可以操纵数据,并获得所需的输出,而无需实际编写任何代码。
具有这种特性的工具很多,但是 Danfo.js 的酷之处在于用 JavaScript 中的工具构建整个应用。事实上,在不调用服务器的情况下在浏览器中完成所有操作是相当惊人的。
本章的目标是展示如何使用 Danfo.js 和 React.js 构建这样的环境,另外,请注意,这里使用的工具(除了 Danfo.js 之外)对于构建应用并不是强制性的;这些只是我相当熟悉的工具。
本章将涵盖以下主题:
- 设置项目环境
- 构建和设计应用
- 应用布局和
DataTable
组件 - 创建不同的
DataFrame
操作组件 - 实现
Chart
组件
技术要求
以下是本章的基本环境和知识要求:
- 像 Chrome 这样的现代网络浏览器
- 合适的代码编辑器,如 VScode
- 已安装 Node.js
tailwindcss
和React-chart-js
的一点知识- 需要了解 React.js 的基础知识。要温习 React.js,请查看https://reactjs.org/docs/hello-world.html官方网站
- 本章代码可用,可从 GitHubhttps://GitHub . com/PacktPublishing/Building-Data-Driven-Applications-with-danfo . js/tree/main/chapter 08处克隆
设置项目环境
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
文件夹的结构:
图 8.1–react . js 目录结构
安装完成后,我们始终可以使用以下命令启动 app(假设您不在终端的data-art
目录中):
$ cd data-art
$ yarn start
以下命令将启动应用服务器,并输出终端中运行应用的服务器端口:
图 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 应用时始终注入tailwindcss
和autoprefixer
,如下面的代码所示:
// 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
并保存,您应该可以直接在浏览器中看到正在进行的更改,如下图所示:
图 8.3–反应应用
让我们测试一下我们的tailwindcss
配置,确保设置正确。我们将通过在前面的代码中添加一些样式来做到这一点,如下所示:
function App() {
return (
<div className="max-w-2xl border mx-auto text-3xl mt-60 text-center">
Data-Art
</div>
);
}
css
样式在名为className
的div
属性中声明。首先,我们设置最大宽度和边框,然后沿着 x 轴创建边距(左右边距为auto
),将字体大小声明为text-3xl
,将页边距顶部设置为60
,然后将文本在div
实例内居中。
根据样式,我们应该会看到以下输出:
图 8.4–将 div 和文本居中
代码库已经设置好了,我们准备好实现我们的无代码环境。
在这一节中,我们看到了如何为我们的应用设置一个 React 环境。我们也看到了如何为我们的应用配置tailwindcss
。在下一节中,我们将学习如何构建和设计应用。
构建和设计应用
React.js 有一些 app 设计的核心理念,大部分是把 UI 分解成一个组件层次,也有一个思路是识别你的状态应该生活在哪里。
在这一节中,我们将看到如何用 React.js 设计我们的无代码应用的结构,并考虑应用设计的 React 哲学。有了这个原则,我们会发现在 React 中实现一个基本的 UI 很容易。
首先,让我们了解什么是无代码环境,以及我们想要用它实现什么。无代码环境用于使数据处理和分析更容易,只需点击几个按钮。
我们将创建一个平台,用户可以在其中上传他们的数据,执行分析,并使用代码进行操作,例如:
- 数据帧到数据帧的操作,如
concat
cummax``cumsum
等算术运算- 查询以按列值筛选出数据帧
- 描述数据帧
我们希望能够在不实际编码的情况下完成所有这些工作,一切都将在浏览器中完成。我们还希望能够使用条形图、折线图和饼图,通过数据可视化从数据中获取见解。下图显示了应用设计的草图:
图 8.5–应用结构和设计草图
图 8.5 展示了 app 的结构和设计。 app 分为三个主要组件,如下所示:
Navbar
组件,包含文件上传、条形图、折线图和DataFrame
操作选择字段- 主体包含
Data Table
部件和chart
部件 SideBar
,包含图表和DataFrame
操作的侧面
应用工作流程可以描述如下:
- 首先,上传一个数据文件(
csv
)。 - 通过上传文件,创建第一个
Data Table
。这是一个包含数据框显示的组件。 - 要执行任何操作,如
DataFrame
操作或图表操作,选择数据表,这样我们就可以识别正确的表来执行操作。 - 对于图表操作,单击条形图、折线图或饼图。该点击事件激活图表操作的
Side Plane
。 - 如果选择了
DataFrame
操作,则DataFrame
操作的Side Plane
被激活。 - 当您填写完
Side Plane
中的必要字段后,会创建一个新的图表组件和Data Table
组件。
下图描述了整个工作流程:
图 8.6–应用工作流程
工作流显示了每个组件如何相互响应。比如不上传文件,主体和Side Plane
就看不见了。即使上传了文件,Side Plane
仍保持隐藏状态,只有在特定数据表上执行数据框或图表操作时才会出现。
由此可见,我们需要创建一个状态来管理每当上传文件时主体的激活,还需要创建一个状态来管理在数据表上进行操作时Side Plane
是如何激活的。另外,注意Side Plane
包含两个操作,我们必须根据选择的操作类型显示这些操作的字段。
如果选择的是图表操作,Side Plane
需要显示所选图表的必要字段,无论是条形图、折线图还是饼图,如果选择的是DataFrame
操作,Side Plane
需要显示数据框操作字段,如下图所示:
图 8.7–侧面操作区域
从图 8.7 可以看出,数据表和chart
组件有一个 x 和 y 符号。这表明我们可以有多个数据表或图表组件,并且每个组件都是可拖动的。因此,我们需要创建一个状态来管理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
框创建了应用的布局。这个布局展示了应用的基本组件,分别是Nav
、Main Body
和Side Plane
。
注意
教程中使用的css
实例就不解释了。我们的主要重点是构建应用的功能。
如果一切顺利,我们应该得到如下输出:
图 8.8–应用布局
我们已经布置好了应用。让我们继续实现DataTable
组件来显示所有DataFrame
操作的结果。
实现数据表组件
一个DataTable
组件是负责数据表的显示。对于每个数据框操作,我们生成一个新数据表,显示操作结果,如下图所示:
图 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
组件:
-
我们进口必要的包装:
js import React from "react"; import ReactTable from 'react-table-v6' import Draggable from 'react-draggable'; import 'react-table-v6/react-table.css'
-
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 thereact-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
是数据中的键。 -
我们需要将数据表值转换成
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 设计中,我们看到任何操作的发生,都需要先上传一个文件。通过上传文件,我们创建了将被DataTable
和chart
组件使用的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
组件状态、文件上传和状态更新:
-
我们进口
React
和一个名为useState
的反应钩:js import React, { useState } from 'react';
-
我们导入
read_csv
方法,用于读取上传的 CSV 文件:js import { read_csv } from 'danfojs/src/io/reader' // step 2
-
我们创建一个状态来存储生成的每个
DataTable
组件的数据列表:js const [dataComp, setDataComp] = useState([])
-
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
会在计算上很昂贵。此外,
columns
和values
被存储起来,以便在我们想要从react-table
组件生成一个表时,可以方便地访问。但是,这仍然感觉是多余的,作为个人练习(在本节末尾列出的待办事项列表中),你可以继续清理它。 -
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) }
以下截图显示了应用的更新用户界面:
图 8.10–文件上传的更新用户界面
上传文件后,我们应该会在浏览器控制台中看到以下输出:
图 8.11–数据组件状态输出
我们已经为每个DataTable
组件设置了文件上传和状态管理。让我们将创建的DataTables
组件集成到应用中。
将数据表组件集成到 App.js 中
App.js
将通过以下步骤更新:
-
我们导入
DataTables
组件并创建compIndex
状态,这使我们能够存储我们想要在时刻处理的DataTable
组件的索引:js . . . . . . . . . . . . import DataTables from './components/DataTables'; function App() { . . . . . . . . . . const [compIndex, setCompIndex] = useState() . . . . . . . . . . }
-
We then add the
DataTables
component to theApp
component:js <div> {(dataComp.length > 0) && <DataTables datacomp={dataComp} setCompIndex={setCompIndex} /> } </div>
为了启用
DataTable
组件可见性,我们检查dataComp
状态是否为空。上传文件前,如果dataComp
状态为空,DataTable
组件将不可见。一旦文件被更新,DataTable
组件变得可见,因为dataComp
状态不再为空。上传文件后,前面的代码应该会给出以下输出:
图 8.12–文件上传时数据表组件的显示
在这一节中,我们讨论了文件上传和DataTable
的创建和管理,并看到了如何管理状态。在下一节中,我们将实现不同的DataFrame
操作组件,并为DataFrame
操作实现Side Plane
。
创建不同的数据框操作组件
在本节中,我们将创建不同的DataFrame
操作组件,并为DataFrame
操作组件实现Side Plane
。Danfo.js 包含很多DataFrame
操作。如果我们要为每个组件设计一个组件,这将是非常紧张和多余的。
为了防止为每个DataFrame
方法创建组件,我们根据每个DataFrame
操作的(关键字)参数,也就是说,根据传递给它们的变量,对它们进行分组。例如,有一些DataFrame
方法只接受操作轴,因此我们可以将这些类型的方法组合在一起。
以下是要创建的DataFrame
操作组件的列表以及在这些组件下分组的DataFrame
方法:
- 算术组件:这个包含
DataFrame
方法,它的自变量只有运算轴,可以是1
也可以是0
。对DataFrame
进行算术运算的方法有min
、max
、sum
、std
、var
、sum
、cumsum
、cummax
和cummin
。 - Df2df 组件:包含两个
DataFrame
组件之间的操作,比如一个DataFrame
和一个序列、一个值或一个DataFrame
之间的逻辑操作。进行这些操作的方法是concat
、lt
、gte
、lte
、gt
和neq
。 - 查询组件:这是
DataFrame
查询的组件。 - 描述组件:这是描述
DataFrame
统计的组件。
我们将按照以下顺序从最不复杂的开始查看这些组件的实现:Describe
、query
、Df2df
和Arithmetic
。
实现描述组件
在本节中,我们将实现Describe
组件,并集成Side Plane
组件。
在src/Components/
目录下,我们再创建一个名为Side Planes
的文件夹。该文件夹将包含DataFrame
操作的所有组件。
在Side Planes/
文件夹中,让我们创建一个名为Describe.js
的".js"
文件,并按照以下步骤进行更新:
-
我们创建
Describe
功能组件,采用dataComp
状态和setDataComp
状态功能,用生成的DataFrame
:js export default function Describe({ dataComp, setDataComp}) { }
更新
dataComp
状态 2. We create a button namedDescribe
:js return ( <div> <button onClick={()=> describe()} className="bg-blue-700 text-white rounded-sm p-2">Describe</button> </div> )
Describe
组件有一个按钮接口,因为它不接受任何参数。该按钮有一个onClick
事件,只要点击该按钮就会触发Describe
功能。 -
We then implement the
describe()
function, which is triggered anytime thedescribe
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
状态获取包含DataFrame
的df
键,然后调用describe
方法。从由
describe
操作生成的DataFrame
中,我们获得列,并向列名列表添加索引。索引添加在列表的开头;这样做是因为需要从describe
方法生成的索引来捕获数据中的每一行。接下来,我们获得
DataFrame
值,循环通过它,并将索引值添加到获得的DataFrame
值。 -
我们用新生成的列、值和
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,
中,如下代码所示:
-
我们导入
SidePlane
组件:js import { SidePlane } from './components/SidePlanes'
-
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
操作时更新dfOpsType
和showSidePlane
状态。 -
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
操作的结果:
图 8.13–描述数据表上的操作
在前面的截图中,左上角的数据表是上传文件时生成的,右下角的DataFrame
是Describe
操作的结果。
在本节中,我们看到了如何实现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.js
的components/Side Planes/
文件夹中创建一个文件,并按照以下步骤更新它:
-
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
钩子变量,它使我们能够获得输入到以下输入字段的当前值:列字段(取列名到查询)、逻辑字段(取要用于查询的逻辑值)和值字段(取要用于查询所选列的值)。 -
We then update the
Query
component with the following code:js const columns = dataComp.columns const logics = [">", "<", "<=", ">=", "==", "!="]
在前面的代码中,我们获得了当前
Data Table
的DataFrame
中可用的列名。此列名将用于填充选择字段,用户可以选择要查询的列。我们还创建了一个符号列表,用来描述我们想要执行的逻辑操作的类型。该符号还将用于填充选择字段,用户可以在其中选择用于查询的逻辑操作。
-
创建
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
中的上述代码:
-
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
来跟踪选定的列名。 -
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 选择字段中,以获得选定的逻辑运算符。 -
然后,我们为查询值
js <div> <span className="mr-2">to</span> <input ref={valuesRef} placeholder="value" className="border"/> </div>
创建
input
字段 4. We create abutton
class name to make a call to thequery
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
组件。如果我们对上传的文件执行查询操作,应该会得到以下结果:
图 8.14–对列 C 运行查询操作,检查其值是否大于 20
在本节中,我们创建了一个query
组件。在下一节中,我们将研究为操作创建一个组件,包括DataFrame
-到- DataFrame
操作、序列和标量值。
实现 Df2df 组件
在本节中,我们将实现一个组件,用于在一个DataFrame
和另一个DataFrame
、Series
和Scalar
值之间执行操作。
注意
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 中有不同的组件执行DataFrame
和Series
以及DataFrame
和Scalar
值之间的操作。为了避免必须为这些方法创建一个组件,我们可以将它们组合在一起形成一个组件。
我们计划分组的DataFrame
方法列表如下:
- 小于(
df.lt
) - 大于(
df.gt
) - 不等于(
df.ne
) - 等于(
df.eq
) - 大于或等于(
df.ge
) - 加法(
df.add
) - 减法(
df.sub
) - 乘法(
df.mul
- 除法(
df.div
) - 功率(
df.pow
)
在前面的方法列表中,一个常见的属性是它们都接受相同类型的参数,即值(可以是DataFrame
、Series
或scalar
值)和要执行操作的轴。
如果我们看一下DataFrame
concat
方法,它也接受与前面列表中的方法类似的相同模式的参数。唯一不同的是,对于concat
方法来说,df_list
参数是一组DataFrames
。
让我们在名为Df2df.js
的Side Planes/
文件夹中创建一个文件。在本文件中,我们将通过以下步骤实现Df2df
组件:
-
First, we import
concat
from Danfo.js, and then create theDf2df
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
列表。 -
We also need to create a function to perform the
Df2df
operation whenever thesubmit
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 . . . . . . . . . . . . . . }
我们从属于每个输入字段的所有参考变量中获得值。
-
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
列表,而不仅仅是DataFrame
或Series
。我们利用
eval
功能防止写入多个if
条件来检查调用哪个DataFrame
操作。 -
我们执行
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 组件接口
让我们按照以下步骤更新界面的代码:
-
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
操作。 -
We then create an
input
field to select theDataFrame
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
状态。 -
然后我们创建一个
input
字段来输入我们的值;在这种情况下,我们在DataFrame
和Scalar
值之间执行操作:js <div> <span>input a value</span> <input ref={inpRef} className="border" /> </div>
-
我们创建一个
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>
-
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 文件的上传:
图 8.15–上传内容相同的 CSV 文件
下图显示了在选定的Data Table
上执行Df2df
操作(具体为concat
操作)的输出:
图 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
组件:
-
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
数组,将所有这些操作(Series
和DataFrame
)存储在一起。 -
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.value
和axisRef.current.value
中获取值。我们也检查选择的操作是否属于seriesOps
。如果是这样,我们执行选定的操作。 -
We perform a
DataFrame
operation if the operation does not belong toseriesOps
:```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
上执行算术运算的例子:
图 8.17–算术运算
在本节中,我们讨论并实现了不同的DataFrame
操作作为一个反应组件。我们能够将一些方法组织成单个组件,以防止为每个操作创建组件。
在下一节中,我们将为不同的可视化实现一个chart
组件。
实现图表组件
在这一节中,我们将创建chart
组件来显示常见和简单的图表,如条形图、折线图和饼图。然后我们将实现图表Side Plane
来启用图表组件变量的设置。
注意
实现的Chart
和ChartPlane
组件的代码可在此处获得: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
组件将通过以下步骤实现:
-
We import our desired plotting component from
react-chartjs-2
, and then create theChart
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
组件接收以下道具:labels
、dataset
和type
。labels
表示列名,dataset
表示dataComp
值,type
表示我们要绘制的图表类型。在
Chart
组件中,我们创建了一个名为data
的变量,它是一个按照react-chartjs-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 theChart
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
组件,如LineChart
和PieChart
中的react-chartjs-2
。
要深入了解react-chartjs-2
,您可以查看这里的文档:https://github.com/reactchartjs/react-chartjs-2。
实现图表平面组件
我们已经创建了chart
组件,所以现在让我们创建图表Side Plane
。在components/
文件夹中,我们按照以下步骤创建一个名为ChartPlane.js
的文件:
-
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 轴包含数据。
-
We create the UI for the
ChartPlane
component. Depending on how we've designed the UI for other components, thex
andy
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>
-
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 }) }
xVal
和yVal
是 x 轴和 y 轴的输入字段值。创建了labels
和data
变量,以包含来自xVal
和yVal
的相应列的值。标签和数据随后用于更新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.js
的components/
文件夹中创建一个文件。将以下代码添加到文件中:
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
组件,然后创建一个包含以下chartComp
和setChartComp
道具的ChartViz
组件。我们遍历chartComp
状态,并将每个状态值作为一个道具传递给chart
组件。
将 ChartViz 和 ChartPlane 集成到 App.js 中
现在我们已经完成了chart
组件的所有必要部分。让我们更新我们的App.js
组件,根据激活chart
组件,步骤如下:
-
我们将
ChartViz
和ChartPlane
导入App.js
:js import ChartsViz from './components/ChartsViz' import ChartPlane from './components/ChartPlane'
-
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
中显示的图表列表。 -
We create a function to update the
chartType
component and theSide 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
。 -
We loop the
charts
variable in thenav
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
状态不为空,即是否没有上传文件,来禁用按钮。 -
We call the
ChartViz
component and pass in the necessary props:js {(chartComp.length > 0) && <ChartsViz chartComp={chartComp} setChartComp={setChartComp} /> }
我们检查
chartComp
状态不为空。如果不是,我们调用ChartViz
组件,然后显示创建的图表。 -
然后我们添加
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
上绘制条形图、折线图和饼图来更新图表:
图 8.18–显示的图表组件
在本节中,我们实现了ChartComponent
和ChartPlane
。我们利用React-chart-js
来简化每个图表组件的开发。
总结
在这一章中,我们看到了如何创建一个无代码环境,您可以只上传数据,然后立即开始处理和进行数据分析。我们还看到了如何将 Danfo.js 中的每个DataFrame
方法转换成一个 React 组件。这提供了将所有的 Danfo.js 方法转换成 React 组件的能力,因此为 Danfo.js 创建了 React 组件库
此外,我们还看到了如何为应用设计流程,以及如何在 React 中管理状态。即使创建的一些状态是多余的,这也是您贡献和更新应用以使其健壮的机会。如果您可以更新应用,使其可以删除、更新和保存正在进行的每个操作,这将使应用健壮,甚至可以投入生产。
在下一章中,我们将介绍机器学习。这一章将以最简单的形式讲述机器学习背后的基本思想。
版权属于:月萌API www.moonapi.com,转载请注明出处