@@ -4,6 +4,7 @@ import ( | |||
"encoding/json" | |||
"github.com/casbin/casbase/object" | |||
"github.com/casbin/casbase/util" | |||
) | |||
func (c *ApiController) GetGlobalDatasets() { | |||
@@ -27,8 +28,9 @@ func (c *ApiController) GetDataset() { | |||
func (c *ApiController) GetDatasetGraph() { | |||
id := c.Input().Get("id") | |||
clusterNumber := util.ParseInt(c.Input().Get("clusterNumber")) | |||
c.Data["json"] = object.GetDatasetGraph(id) | |||
c.Data["json"] = object.GetDatasetGraph(id, clusterNumber) | |||
c.ServeJSON() | |||
} | |||
@@ -15,8 +15,10 @@ func init() { | |||
graphCache = map[string]*Graph{} | |||
} | |||
func GetDatasetGraph(id string) *Graph { | |||
g, ok := graphCache[id] | |||
func GetDatasetGraph(id string, clusterNumber int) *Graph { | |||
cacheId := fmt.Sprintf("%s|%d", id, clusterNumber) | |||
g, ok := graphCache[cacheId] | |||
if ok { | |||
return g | |||
} | |||
@@ -26,8 +28,10 @@ func GetDatasetGraph(id string) *Graph { | |||
return nil | |||
} | |||
runKmeans(dataset.Vectors, clusterNumber) | |||
g = generateGraph(dataset.Vectors) | |||
graphCache[id] = g | |||
graphCache[cacheId] = g | |||
return g | |||
} | |||
@@ -61,7 +65,7 @@ func getNodeColor(weight int) string { | |||
return fmt.Sprintf("rgb(%d,%d,%d)", myColor.R, myColor.G, myColor.B) | |||
} | |||
var DistanceLimit = 11 | |||
var DistanceLimit = 14 | |||
func generateGraph(vectors []*Vector) *Graph { | |||
vectors = refineVectors(vectors) | |||
@@ -18,7 +18,7 @@ func fa2Str(floatArray []float64) string { | |||
return strings.Join(sData, "|") | |||
} | |||
func runKmeans(vectors []*Vector) { | |||
func runKmeans(vectors []*Vector, clusterNumber int) { | |||
vectorMap := map[string]*Vector{} | |||
var d clusters.Observations | |||
@@ -34,7 +34,7 @@ func runKmeans(vectors []*Vector) { | |||
} | |||
km := kmeans.New() | |||
cs, err := km.Partition(d, 100) | |||
cs, err := km.Partition(d, clusterNumber) | |||
if err != nil { | |||
panic(err) | |||
} | |||
@@ -61,7 +61,7 @@ func runKmeans(vectors []*Vector) { | |||
func updateDatasetVectorCategories(owner string, datasetName string) { | |||
dataset := getDataset(owner, datasetName) | |||
runKmeans(dataset.Vectors) | |||
runKmeans(dataset.Vectors, 100) | |||
UpdateDataset(dataset.GetId(), dataset) | |||
} |
@@ -1,11 +1,13 @@ | |||
import React from "react"; | |||
import {Card, Col, Empty, List, Row, Slider, Spin, Switch, Tooltip} from "antd"; | |||
import {Button, Card, Col, Empty, InputNumber, List, Row, Select, Slider, Spin, Switch, Tooltip} from "antd"; | |||
import ForceGraph2D from 'react-force-graph-2d'; | |||
import ForceGraph3D from 'react-force-graph-3d'; | |||
import * as d3 from "d3-force"; | |||
import * as DatasetBackend from "./backend/DatasetBackend"; | |||
import i18next from "i18next"; | |||
const { Option } = Select; | |||
let fg = React.createRef(); | |||
class ForceGraph extends React.Component { | |||
@@ -43,6 +45,7 @@ class Dataset extends React.Component { | |||
// particlePercent: 100, | |||
strength: 20, | |||
distanceMax: 100, | |||
clusterNumber: 100, | |||
selectedType: null, | |||
selectedId: null, | |||
selectedIds: [], | |||
@@ -54,11 +57,11 @@ class Dataset extends React.Component { | |||
} | |||
componentDidUpdate(prevProps, prevState, snapshot) { | |||
fg.current.d3Force('collision', d3.forceCollide(15)); | |||
fg.current?.d3Force('collision', d3.forceCollide(15)); | |||
} | |||
getDatasetGraph() { | |||
DatasetBackend.getDatasetGraph("admin", this.state.datasetName) | |||
DatasetBackend.getDatasetGraph("admin", this.state.datasetName, this.state.clusterNumber) | |||
.then((graph) => { | |||
this.setState({ | |||
graph: graph, | |||
@@ -247,6 +250,52 @@ class Dataset extends React.Component { | |||
</Col> | |||
</Row> | |||
</Card> | |||
<Card style={{marginTop: "20px"}} size="small" title={ | |||
<div> | |||
聚类参数 | |||
</div> | |||
} type="inner"> | |||
<Row> | |||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={8}> | |||
算法: | |||
</Col> | |||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={16}> | |||
<Select virtual={false} style={{width: '100%'}} value={"K-Means"} onChange={(value => { | |||
this.setState({ | |||
algorithm: value, | |||
}); | |||
})}> | |||
{ | |||
[ | |||
{id: 'K-Means', name: 'K-Means'}, | |||
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>) | |||
} | |||
</Select> | |||
</Col> | |||
</Row> | |||
<Row> | |||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}> | |||
聚类个数: | |||
</Col> | |||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}> | |||
<InputNumber style={{width: "100%"}} min={2} max={this.state.graph?.nodes.length} step={1} value={this.state.clusterNumber} onChange={value => { | |||
this.setState({ | |||
clusterNumber: value, | |||
}); | |||
}} /> | |||
</Col> | |||
</Row> | |||
<Row style={{textAlign: "center", paddingTop: '10px'}}> | |||
<Button style={{margin: 'auto'}} type="primary" onClick={() => { | |||
this.setState({ | |||
graph: null, | |||
}); | |||
this.getDatasetGraph(); | |||
}}> | |||
重新聚类 | |||
</Button> | |||
</Row> | |||
</Card> | |||
</div> | |||
</div> | |||
) | |||
@@ -21,8 +21,8 @@ export function getDataset(owner, name) { | |||
}).then(res => res.json()); | |||
} | |||
export function getDatasetGraph(owner, name) { | |||
return fetch(`${Setting.ServerUrl}/api/get-dataset-graph?id=${owner}/${encodeURIComponent(name)}`, { | |||
export function getDatasetGraph(owner, name, clusterNumber) { | |||
return fetch(`${Setting.ServerUrl}/api/get-dataset-graph?id=${owner}/${encodeURIComponent(name)}&clusterNumber=${clusterNumber}`, { | |||
method: "GET", | |||
credentials: "include" | |||
}).then(res => res.json()); | |||