timeseries commit

This commit is contained in:
Ali Abid 2021-07-28 11:21:28 -07:00
parent 0d5057f6b8
commit 3bfeb4105f
12 changed files with 6177 additions and 42 deletions

23
demo/fraud_detector.py Normal file
View File

@ -0,0 +1,23 @@
import gradio as gr
import pandas as pd
import random
def fraud_detector(card_activity, categories, sensitivity):
activity_range = random.randint(0, 100)
return {"fraud": activity_range / 100., "not fraud": 1 - activity_range / 100.}
iface = gr.Interface(fraud_detector,
[
gr.inputs.Timeseries(
x="time",
y=["retail", "food", "other"]
),
gr.inputs.CheckboxGroup(["retail", "food", "other"], default=["retail", "food", "other"]),
gr.inputs.Slider(1,3)
],
[
gr.outputs.Label(label="Fraud Level"),
]
)
if __name__ == "__main__":
iface.launch()

File diff suppressed because it is too large Load Diff

View File

@ -14,9 +14,11 @@
"fabric": "^4.5.0",
"html2canvas-objectfit-fix": "^1.2.0",
"jspreadsheet-ce": "^4.7.3",
"plotly.js": "^2.3.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-json-tree": "^0.15.0",
"react-plotly.js": "^2.5.1",
"react-scripts": "4.0.3",
"react-webcam": "^5.2.3",
"sass": "^1.32.8",

View File

@ -9,6 +9,7 @@ import { NumberInput, NumberInputExample } from './interfaces/input/number';
import { RadioInput, RadioInputExample } from './interfaces/input/radio';
import { SliderInput, SliderInputExample } from './interfaces/input/slider';
import { TextboxInput, TextboxInputExample } from './interfaces/input/textbox';
import { TimeseriesInput, TimeseriesInputExample } from './interfaces/input/timeseries';
import { VideoInput, VideoInputExample } from './interfaces/input/video';
import { AudioOutput, AudioOutputExample } from './interfaces/output/audio';
@ -36,6 +37,7 @@ let input_component_map = {
"radio": [RadioInput, RadioInputExample],
"slider": [SliderInput, SliderInputExample],
"textbox": [TextboxInput, TextboxInputExample],
"timeseries": [TimeseriesInput, TimeseriesInputExample],
"video": [VideoInput, VideoInputExample],
}
let output_component_map = {

View File

@ -0,0 +1,121 @@
import React from 'react';
import ComponentExample from '../component_example';
import { CSVToArray } from '../../utils';
import Plot from 'react-plotly.js';
class TimeseriesInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.uploader = React.createRef();
this.openFileUpload = this.openFileUpload.bind(this);
this.load_preview_from_files = this.load_preview_from_files.bind(this);
this.load_preview_from_upload = this.load_preview_from_upload.bind(this);
this.load_preview_from_drop = this.load_preview_from_drop.bind(this);
}
handleChange(data) {
this.props.handleChange(data);
}
openFileUpload() {
this.uploader.current.click();
}
render() {
let no_action = (evt) => {
evt.preventDefault();
evt.stopPropagation();
}
if (this.props.value !== null) {
let file = this.props.value[0];
return (
<div className="input_timeseries">
<Plot
data={this.state.y_indices.map((y_index, i) => {
return {
x: this.props.value["data"].map(row => row[this.state.x_index]),
y: this.props.value["data"].map(row => row[y_index]),
type: 'line',
name: this.props.y[i]
}
})}
layout={{
width: 480,
height: 320,
title: {text: "Card Activity"},
xAxis: {title: {text: "Day"}},
yAxis: {title: {text: "Spend"}},
}}
/>
</div>)
} else {
return (
<div className="input_timeseries" onDrag={no_action} onDragStart={no_action} onDragEnd={no_action} onDragOver={no_action} onDragEnter={no_action} onDragLeave={no_action} onDrop={no_action} >
<div className="upload_zone" onClick={this.openFileUpload} onDrop={this.load_preview_from_drop}>
Upload Timeseries CSV
{this.props.x !== null ? <>
<br />
X Column: {this.props.x}
<br />
Y Column: {this.props.y.join(", ")}
</> : false}
</div>
<input className="hidden_upload" type="file" multiple={this.props.file_count === "multiple"} webkitdirectory={this.props.file_count === "directory"} mozdirectory={this.props.file_count === "directory"} ref={this.uploader} onChange={this.load_preview_from_upload} style={{ display: "none" }} />
</div>)
}
}
load_preview_from_drop(evt) {
this.load_preview_from_files(evt.dataTransfer.files)
}
load_preview_from_upload(evt) {
this.load_preview_from_files(evt.target.files);
}
load_file(reader) {
let lines = reader.result;
let headers = null;
let data = null;
if (lines && lines.length > 0) {
let line_array = CSVToArray(lines);
if (line_array.length === 0) {
return;
}
if (this.props.x === null) {
this.setState({ "x_index": 0, "y_indices": [1] })
data = line_array;
} else {
let x_index = line_array[0].indexOf(this.props.x);
let y_indices = this.props.y.map(y_col => line_array[0].indexOf(y_col));
if (x_index === -1) {
alert("Missing x column: " + this.props.x);
return;
}
if (y_indices.includes(-1)) {
alert("Missing y column: " + this.props.y[y_indices.indexOf(-1)]);
return;
}
this.setState({ "x_index": x_index, "y_indices": y_indices })
headers = line_array[0];
data = line_array.slice(1);
}
this.handleChange({ "headers": headers, "data": data });
}
}
load_preview_from_files(files) {
if (!files.length || !window.FileReader) {
return;
}
this.file_data = [];
for (let file of files) {
let ReaderObj = new FileReader()
ReaderObj.readAsBinaryString(file)
ReaderObj.onloadend = this.load_file.bind(this, ReaderObj);
}
}
}
class TimeseriesInputExample extends ComponentExample {
render() {
return <div className="input_file_example">{this.props.value}</div>
}
}
export { TimeseriesInput, TimeseriesInputExample };

View File

@ -388,6 +388,18 @@
@apply text-2xl p-2;
}
}
.input_timeseries {
@apply w-full h-96;
.upload_zone {
@apply border-8 border-gray-300 border-dashed w-full h-full flex justify-center items-center text-3xl text-gray-400 text-center cursor-pointer leading-10;
}
.file_preview_holder {
@apply w-full h-full flex flex-col justify-center items-center relative inline-block;
}
.js-plotly-plot, .plotly.js-plotly-plot * {
position: static !important;
}
}
/* Output Components */
.output_text {
word-break: break-word;

View File

@ -107,3 +107,31 @@ export function array_compare(a1, a2) {
}
return true;
}
export function CSVToArray(strData, strDelimiter) {
strDelimiter = (strDelimiter || ",");
let objPattern = new RegExp(
(
"(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
"(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
"([^\"\\" + strDelimiter + "\\r\\n]*))"
),
"gi"
);
let arrData = [[]];
let arrMatches = null;
while (arrMatches = objPattern.exec(strData)) {
let strMatchedDelimiter = arrMatches[1];
let strMatchedValue = [];
if (strMatchedDelimiter.length && (strMatchedDelimiter != strDelimiter)) {
arrData.push([]);
}
if (arrMatches[2]) {
strMatchedValue = arrMatches[2].replace(new RegExp("\"\"", "g"), "\"");
} else {
strMatchedValue = arrMatches[3];
}
arrData[arrData.length - 1].push(strMatchedValue);
}
return (arrData);
}

View File

@ -32,6 +32,8 @@ gradio/frontend/static/bundle.css.map
gradio/frontend/static/bundle.js
gradio/frontend/static/bundle.js.LICENSE.txt
gradio/frontend/static/bundle.js.map
gradio/frontend/static/css/main.2056a447.css
gradio/frontend/static/css/main.2056a447.css.map
gradio/frontend/static/css/main.337638fd.css
gradio/frontend/static/css/main.337638fd.css.map
gradio/frontend/static/css/main.347bef8c.css
@ -42,6 +44,8 @@ gradio/frontend/static/css/main.bc3b604e.css
gradio/frontend/static/css/main.bc3b604e.css.map
gradio/frontend/static/css/main.c7572f0f.css
gradio/frontend/static/css/main.c7572f0f.css.map
gradio/frontend/static/css/main.c770ef42.css
gradio/frontend/static/css/main.c770ef42.css.map
gradio/frontend/static/media/logo_loading.e93acd82.jpg
test/test_demos.py
test/test_inputs.py

View File

@ -1,15 +1,15 @@
numpy
scipy
matplotlib
pandas
pillow
Flask-Cors>=3.0.8
Flask-Login
Flask>=1.1.1
analytics-python
ffmpy
flask-cachebuster
markdown2
matplotlib
numpy
pandas
paramiko
pillow
pycryptodome
requests
paramiko
analytics-python
Flask>=1.1.1
Flask-Cors>=3.0.8
flask-cachebuster
Flask-Login
scipy

View File

@ -1,17 +1,17 @@
{
"files": {
"main.css": "/static/css/main.337638fd.css",
"main.css": "/static/css/main.c770ef42.css",
"main.js": "/static/bundle.js",
"main.js.map": "/static/bundle.js.map",
"index.html": "/index.html",
"static/bundle.css.map": "/static/bundle.css.map",
"static/bundle.js.LICENSE.txt": "/static/bundle.js.LICENSE.txt",
"static/css/main.337638fd.css.map": "/static/css/main.337638fd.css.map",
"static/css/main.c770ef42.css.map": "/static/css/main.c770ef42.css.map",
"static/media/logo_loading.e93acd82.jpg": "/static/media/logo_loading.e93acd82.jpg"
},
"entrypoints": [
"static/bundle.css",
"static/css/main.337638fd.css",
"static/css/main.c770ef42.css",
"static/bundle.js"
]
}

View File

@ -8,4 +8,4 @@
window.config = {{ config|tojson }};
} catch (e) {
window.config = {};
}</script><script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.1/iframeResizer.contentWindow.min.js"></script><title>Gradio</title><link href="static/bundle.css" rel="stylesheet"><link href="static/css/main.337638fd.css" rel="stylesheet"></head><body><div id="root"></div><script src="static/bundle.js"></script></body></html>
}</script><script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.1/iframeResizer.contentWindow.min.js"></script><title>Gradio</title><link href="static/bundle.css" rel="stylesheet"><link href="static/css/main.c770ef42.css" rel="stylesheet"></head><body><div id="root"></div><script src="static/bundle.js"></script></body></html>

View File

@ -1143,35 +1143,55 @@ class Dataframe(InputComponent):
else:
raise ValueError("Unknown type: " + str(self.type) + ". Please choose from: 'pandas', 'numpy', 'array'.")
# def set_interpret_parameters(self):
# """
# Calculates interpretation score of each cell in the Dataframe by using a "leave one out" method to calculate the score of each cell by removing the cell and measuring the delta of the output value.
# """
# return self
# def get_interpretation_neighbors(self, x):
# x = pd.DataFrame(x)
# leave_one_out_sets = []
# shape = x.shape
# for i in range(shape[0]):
# for j in range(shape[1]):
# scalar = x.iloc[i, j]
# leave_one_out_df = x.copy()
# if is_bool_dtype(scalar):
# leave_one_out_df.iloc[i, j] = not scalar
# elif is_numeric_dtype(scalar):
# leave_one_out_df.iloc[i, j] = 0
# elif is_string_dtype(scalar):
# leave_one_out_df.iloc[i, j] = ""
# leave_one_out_sets.append(leave_one_out_df.values.tolist())
# return leave_one_out_sets, {"shape": x.shape}
def embed(self, x):
raise NotImplementedError("DataFrame doesn't currently support embeddings")
def save_flagged(self, dir, label, data, encryption_key):
"""
Returns: (List[List[Union[str, float]]]) 2D array
"""
return json.dumps(data)
def restore_flagged(self, data):
return json.loads(data)
class Timeseries(InputComponent):
"""
Component accepts pandas.DataFrame uploaded as a timeseries csv file.
Input type: pandas.DataFrame
"""
def __init__(self, x=None, y=None, label=None):
"""
Parameters:
x (str): Column name of x (time) series. None if csv has no headers, in which case first column is x series.
y (Union[str, List[str]]): Column name of y series, or list of column names if multiple series. None if csv has no headers, in which case every column after first is a y series.
label (str): component name in interface.
"""
self.x = x
if isinstance(y, str):
y = [y]
self.y = y
super().__init__(label)
def get_template_context(self):
return {
"x": self.x,
"y": self.y,
**super().get_template_context()
}
@classmethod
def get_shortcut_implementations(cls):
return {
"timeseries": {},
}
def preprocess(self, x):
return pd.DataFrame(data=x["data"], columns=x["headers"])
# def get_interpretation_scores(self, x, neighbors, scores, shape):
# """
# Returns:
# (List[List[float]]): A 2D array where each value corrseponds to the interpretation score of each cell.
# """
# return np.array(scores).reshape((shape)).tolist()
def embed(self, x):
raise NotImplementedError("DataFrame doesn't currently support embeddings")