Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Fractalis
fractal.js
Commits
96b1dc31
Commit
96b1dc31
authored
Jul 25, 2017
by
Sascha Herzinger
Browse files
basic heatmap code
parent
6a674264
Changes
7
Hide whitespace changes
Inline
Side-by-side
src/services/chart-manager.js
View file @
96b1dc31
import
Vue
from
'
vue
'
import
CorrelationAnalysis
from
'
../vue/charts/CorrelationAnalysis.vue
'
import
Boxplot
from
'
../vue/charts/Boxplot.vue
'
import
Heatmap
from
'
../vue/charts/Heatmap.vue
'
export
default
class
{
constructor
()
{
this
.
AVAILABLE_CHARTS
=
{
[
CorrelationAnalysis
.
name
]:
CorrelationAnalysis
,
[
Boxplot
.
name
]:
Boxplot
[
Boxplot
.
name
]:
Boxplot
,
[
Heatmap
.
name
]:
Heatmap
}
}
...
...
src/vue/charts/Boxplot.vue
View file @
96b1dc31
...
...
@@ -111,12 +111,12 @@
<
script
>
import
DataBox
from
'
../components/DataBox.vue
'
import
store
from
'
../../store/store
'
import
r
equestHandling
from
'
../mixins/run-analysis
'
import
r
unAnalysis
from
'
../mixins/run-analysis
'
import
*
as
d3
from
'
d3
'
import
{
TweenLite
}
from
'
gsap
'
import
TaskView
from
'
../components/TaskView.vue
'
import
deepFreeze
from
'
deep-freeze-strict
'
import
u
til
s
from
'
../mixins/utils
'
import
{
truncateTextUn
til
}
from
'
../mixins/utils
'
import
tooltip
from
'
../directives/tooltip
'
export
default
{
name
:
'
boxplot
'
,
...
...
@@ -265,7 +265,7 @@
axis
()
{
const
x
=
d3
.
axisBottom
(
this
.
scales
.
x
).
tickFormat
(
d
=>
{
// noinspection JSSuspiciousNameCombination
return
utils
.
truncateTextUntil
({
text
:
d
,
font
:
`14px Roboto`
,
maxWidth
:
this
.
margin
.
bottom
})
return
truncateTextUntil
({
text
:
d
,
font
:
`14px Roboto`
,
maxWidth
:
this
.
margin
.
bottom
})
})
const
y
=
d3
.
axisLeft
(
this
.
scales
.
y
)
return
{
x
,
y
}
...
...
@@ -344,14 +344,13 @@
},
runAnalysisWrapper
({
args
})
{
// function made available via requestHandling mixin
this
.
runAnalysis
({
task_name
:
'
compute-boxplot
'
,
args
})
runAnalysis
({
task_name
:
'
compute-boxplot
'
,
args
})
.
then
(
response
=>
{
const
results
=
JSON
.
parse
(
response
)
const
data
=
JSON
.
parse
(
results
.
data
)
results
.
data
=
Object
.
keys
(
data
).
map
(
key
=>
data
[
key
])
deepFreeze
(
results
)
// massively improve performance by telling Vue that the objects properties won't change
this
.
results
=
results
this
.
handleResize
()
})
.
catch
(
error
=>
console
.
error
(
error
))
}
...
...
@@ -360,9 +359,6 @@
DataBox
,
TaskView
},
mixins
:
[
requestHandling
],
directives
:
{
tooltip
},
...
...
src/vue/charts/CorrelationAnalysis.vue
View file @
96b1dc31
...
...
@@ -116,7 +116,7 @@
<
script
>
import
DataBox
from
'
../components/DataBox.vue
'
import
store
from
'
../../store/store
'
import
r
equestHandling
from
'
../mixins/run-analysis
'
import
r
unAnalysis
from
'
../mixins/run-analysis
'
import
*
as
d3
from
'
d3
'
import
{
TweenLite
}
from
'
gsap
'
import
tooltip
from
'
../directives/tooltip.js
'
...
...
@@ -456,16 +456,13 @@
DataBox
,
TaskView
}
,
mixins
:
[
requestHandling
],
directives
:
{
tooltip
}
,
methods
:
{
runAnalysisWrapper
({
init
,
args
}
)
{
// function made available via requestHandling mixin
this
.
runAnalysis
({
task_name
:
'
compute-correlation
'
,
args
}
)
runAnalysis
({
task_name
:
'
compute-correlation
'
,
args
}
)
.
then
(
response
=>
{
const
results
=
JSON
.
parse
(
response
)
const
data
=
JSON
.
parse
(
results
.
data
)
...
...
src/vue/charts/Heatmap.vue
0 → 100644
View file @
96b1dc31
<
template
>
<div
:class=
"`fjs-heatmap fjs-vm-uid-$
{this._uid}`">
<div
class=
"fjs-data-box-container"
>
<data-box
class=
"fjs-data-box"
header=
"Numerical Array Data"
dataType=
"numerical_array"
v-on:update=
"update_numericArrayData"
>
</data-box>
</div>
<div
class=
"fjs-parameter-container"
>
</div>
<div
class=
"fjs-vis-container"
>
<svg
:height=
"height"
:width=
"width"
>
<g
:transform=
"`translate($
{margin.left}, ${margin.top})`">
<rect
class=
"fjs-cell"
:x=
"scales.x(d.id)"
:y=
"scales.y(d.variable)"
:height=
"width / uniqueIds.length"
:width=
"width / uniqueIds.length"
v-for=
"d in results.data"
>
</rect>
</g>
</svg>
</div>
</div>
</
template
>
<
script
>
import
DataBox
from
'
../components/DataBox.vue
'
import
store
from
'
../../store/store
'
import
runAnalysis
from
'
../mixins/run-analysis
'
import
*
as
d3
from
'
d3
'
import
tooltip
from
'
../directives/tooltip.js
'
import
TaskView
from
'
../components/TaskView.vue
'
import
deepFreeze
from
'
deep-freeze-strict
'
export
default
{
name
:
'
heatmap
'
,
data
()
{
return
{
width
:
500
,
height
:
500
,
numericArrayDataIds
:
[],
results
:
{}
}
},
computed
:
{
args
()
{
return
{
numerical_arrays
:
this
.
numericArrayDataIds
,
numericals
:
[],
categoricals
:
[]
}
},
margin
()
{
const
left
=
50
const
top
=
50
const
right
=
50
const
bottom
=
50
return
{
left
,
top
,
right
,
bottom
}
},
padded
()
{
const
width
=
this
.
width
-
this
.
margin
.
left
-
this
.
margin
.
right
const
height
=
this
.
height
-
this
.
margin
.
top
-
this
.
margin
.
bottom
return
{
width
,
height
}
},
uniqueIds
()
{
return
[...
new
Set
(
this
.
results
.
data
.
map
(
d
=>
d
.
id
))]
},
uniqueVariables
()
{
return
[...
new
Set
(
this
.
results
.
data
.
map
(
d
=>
d
.
variable
))]
},
scales
()
{
const
x
=
d3
.
scaleOrdinal
()
.
domain
(
this
.
uniqueIds
)
.
range
(
this
.
uniqueIds
.
map
((
d
,
i
)
=>
i
*
this
.
padded
.
width
/
this
.
uniqueIds
.
length
))
const
y
=
d3
.
scaleOrdinal
()
.
domain
(
this
.
uniqueVariables
)
.
range
(
this
.
uniqueVariables
.
map
((
d
,
i
)
=>
i
*
this
.
padded
.
height
/
this
.
uniqueVariables
.
length
))
return
{
x
,
y
}
}
},
methods
:
{
runAnalysisWrapper
(
args
)
{
runAnalysis
({
task_name
:
'
compute-heatmap
'
,
args
})
.
then
(
response
=>
{
const
results
=
JSON
.
parse
(
response
)
const
data
=
JSON
.
parse
(
results
.
data
)
results
.
data
=
Object
.
keys
(
data
).
map
(
key
=>
data
[
key
])
deepFreeze
(
results
)
// massively improve performance by telling Vue that the objects properties won't change
this
.
results
=
results
})
},
handleResize
()
{
const
container
=
this
.
$el
.
querySelector
(
`.fjs-vm-uid-
${
this
.
_uid
}
.fjs-vis-container svg`
)
// noinspection JSSuspiciousNameCombination
this
.
height
=
container
.
getBoundingClientRect
().
width
this
.
width
=
container
.
getBoundingClientRect
().
width
},
update_numericArrayData
(
ids
)
{
this
.
numericArrayDataIds
=
ids
}
},
watch
:
{
'
args
'
:
{
handler
:
function
(
newArgs
,
oldArgs
)
{
if
(
JSON
.
stringify
(
newArgs
)
!==
JSON
.
stringify
(
oldArgs
))
{
this
.
runAnalysisWrapper
(
newArgs
)
}
}
}
},
mounted
()
{
window
.
addEventListener
(
'
resize
'
,
this
.
handleResize
)
this
.
handleResize
()
},
beforeDestroy
()
{
window
.
removeEventListener
(
'
resize
'
,
this
.
handleResize
)
},
components
:
{
DataBox
,
TaskView
},
directives
:
{
tooltip
}
}
</
script
>
<
style
lang=
"sass"
scoped
>
@import
'./src/assets/base.sass'
*
font-family
:
Roboto
,
sans-serif
.fjs-correlation-analysis
height
:
100%
width
:
100%
display
:
flex
flex-direction
:
column
.fjs-data-box-container
height
:
160px
display
:
flex
justify-content
:
space-around
.fjs-parameter-container
text-align
:
center
.fjs-vis-container
flex
:
1
display
:
flex
svg
flex
:
1
.fjs-cell
stroke
:
#fff
stroke-width
:
2px
shape-rendering
:
crispEdges
</
style
>
\ No newline at end of file
src/vue/mixins/run-analysis.js
View file @
96b1dc31
import
store
from
'
../../store/store
'
export
default
{
methods
:
{
/**
* A helper method to submit an analysis.
* This method returns a promise that resolves into the statistic results of the task once it is finished.
* Please pay special attention to the `args` syntax.
* @param task_name The name to execute. For instance `compute-correlation`
* @param args An object containing all parameters for the task. Strings wrapped in '$' characters are treated as
* data ids. The backend will attempt to replace them with a Python Pandas DataFrame.
* Example args = {method: 'sum', x: '$1234-5678-12345678$'} will replace x with a DataFrame, if available. This
* value is usually retrieved from the data-box component, which will emit an `update` signal containing currently
* selected data ids. Note that Arrays of '$' wrapped strings are also valid.
* @returns {Promise.<void>} An ES6 promise. Resolves into the result of the analysis.
*/
async
runAnalysis
({
task_name
,
args
})
{
function
timeout
(
ms
)
{
return
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
ms
))
}
const
rv1
=
await
store
.
getters
.
requestManager
.
createAnalysis
({
task_name
,
args
})
const
taskID
=
rv1
.
data
.
task_id
/**
* A helper method to submit an analysis.
* This method returns a promise that resolves into the statistic results of the task once it is finished.
* Please pay special attention to the `args` syntax.
* @param task_name The name to execute. For instance `compute-correlation`
* @param args An object containing all parameters for the task. Strings wrapped in '$' characters are treated as
* data ids. The backend will attempt to replace them with a Python Pandas DataFrame.
* Example args = {method: 'sum', x: '$1234-5678-12345678$'} will replace x with a DataFrame, if available. This
* value is usually retrieved from the data-box component, which will emit an `update` signal containing currently
* selected data ids. Note that Arrays of '$' wrapped strings are also valid.
* @returns {Promise.<void>} An ES6 promise. Resolves into the result of the analysis.
*/
async
function
runAnalysis
({
task_name
,
args
})
{
function
timeout
(
ms
)
{
return
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
ms
))
}
const
rv1
=
await
store
.
getters
.
requestManager
.
createAnalysis
({
task_name
,
args
})
const
taskID
=
rv1
.
data
.
task_id
store
.
dispatch
(
'
setTask
'
,
{
taskID
,
taskName
:
task_name
,
taskState
:
'
PENDING
'
})
let
counter
=
0
while
(
counter
++
<
1000
)
{
await
timeout
(
400
)
const
rv2
=
await
store
.
getters
.
requestManager
.
getAnalysisStatus
({
taskID
})
const
taskInfo
=
rv2
.
data
if
(
taskInfo
.
state
===
'
SUCCESS
'
)
{
store
.
dispatch
(
'
setTask
'
,
{
taskID
,
taskName
:
task_name
,
taskState
:
taskInfo
.
state
})
return
taskInfo
.
result
}
else
if
(
taskInfo
.
state
===
'
FAILURE
'
)
{
store
.
dispatch
(
'
setTask
'
,
{
taskID
,
taskName
:
task_name
,
taskState
:
'
PENDING
'
taskState
:
taskInfo
.
state
,
taskMessage
:
taskInfo
.
result
})
let
counter
=
0
while
(
counter
++
<
1000
)
{
await
timeout
(
400
)
const
rv2
=
await
store
.
getters
.
requestManager
.
getAnalysisStatus
({
taskID
})
const
taskInfo
=
rv2
.
data
if
(
taskInfo
.
state
===
'
SUCCESS
'
)
{
store
.
dispatch
(
'
setTask
'
,
{
taskID
,
taskName
:
task_name
,
taskState
:
taskInfo
.
state
})
return
taskInfo
.
result
}
else
if
(
taskInfo
.
state
===
'
FAILURE
'
)
{
store
.
dispatch
(
'
setTask
'
,
{
taskID
,
taskName
:
task_name
,
taskState
:
taskInfo
.
state
,
taskMessage
:
taskInfo
.
result
})
throw
new
Error
(
taskInfo
.
result
)
}
else
if
(
taskInfo
.
state
===
'
PENDING
'
)
{
store
.
dispatch
(
'
setTask
'
,
{
taskID
,
taskName
:
task_name
,
taskState
:
taskInfo
.
state
})
}
else
{
throw
new
Error
(
`Analysis Task has unknown state:
${
taskInfo
.
state
}
`
)
}
}
throw
new
Error
(
taskInfo
.
result
)
}
else
if
(
taskInfo
.
state
===
'
PENDING
'
)
{
store
.
dispatch
(
'
setTask
'
,
{
taskID
,
taskName
:
task_name
,
taskState
:
taskInfo
.
state
})
}
else
{
throw
new
Error
(
`Analysis Task has unknown state:
${
taskInfo
.
state
}
`
)
}
}
}
// This code is here because of: https://github.com/babel/babel/issues/3786
export
default
runAnalysis
src/vue/mixins/utils.js
View file @
96b1dc31
export
default
{
/**
* https://stackoverflow.com/questions/5723154/truncate-a-string-in-the-middle-with-javascript
*/
truncate
({
text
,
strLen
,
separator
})
{
if
(
text
.
length
<=
strLen
)
return
text
/**
* https://stackoverflow.com/questions/5723154/truncate-a-string-in-the-middle-with-javascript
*/
export
function
truncate
({
text
,
strLen
,
separator
})
{
if
(
text
.
length
<=
strLen
)
return
text
separator
=
separator
||
'
...
'
separator
=
separator
||
'
...
'
const
sepLen
=
separator
.
length
const
charsToShow
=
strLen
-
sepLen
const
frontChars
=
Math
.
ceil
(
charsToShow
/
2
)
const
backChars
=
Math
.
floor
(
charsToShow
/
2
)
const
sepLen
=
separator
.
length
const
charsToShow
=
strLen
-
sepLen
const
frontChars
=
Math
.
ceil
(
charsToShow
/
2
)
const
backChars
=
Math
.
floor
(
charsToShow
/
2
)
return
text
.
substr
(
0
,
frontChars
)
+
separator
+
text
.
substr
(
text
.
length
-
backChars
)
},
/**
* https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript
*/
getTextWidth
({
text
,
font
})
{
// re-use canvas object for better performance
const
canvas
=
this
.
getTextWidth
.
canvas
||
(
this
.
getTextWidth
.
canvas
=
document
.
createElement
(
'
canvas
'
))
const
c
ontext
=
canvas
.
getContext
(
'
2d
'
)
context
.
font
=
font
con
st
metrics
=
c
ont
ext
.
measureText
(
text
)
return
metrics
.
width
},
truncateTextUntil
({
text
,
font
,
maxWidth
})
{
while
(
this
.
getTextWidth
({
text
,
font
})
>
maxWidth
)
{
text
=
this
.
truncate
({
text
,
strLen
:
text
.
length
-
5
})
}
return
text
return
text
.
substr
(
0
,
frontChars
)
+
separator
+
text
.
substr
(
text
.
length
-
backChars
)
}
/**
* https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript
*/
export
function
getTextWidth
({
text
,
font
})
{
// re-use canvas object for better performance
const
c
anvas
=
getTextWidth
.
canvas
||
(
getTextWidth
.
canvas
=
document
.
createElement
(
'
canvas
'
)
)
const
context
=
canvas
.
getContext
(
'
2d
'
)
con
text
.
font
=
f
ont
const
metrics
=
context
.
measureText
(
text
)
return
metrics
.
width
}
export
function
truncateTextUntil
({
text
,
font
,
maxWidth
})
{
while
(
getTextWidth
({
text
,
font
})
>
maxWidth
)
{
text
=
truncate
({
text
,
strLen
:
text
.
length
-
5
})
}
return
text
}
test/charts/heatmap/heatmap.html
0 → 100644
View file @
96b1dc31
<!doctype html>
<head>
<script
src=
"http://localhost:8080/credentials.js"
></script>
<script
src=
"http://localhost:8080/fractal.js"
></script>
</head>
<body>
<input
type=
"button"
onclick=
"loadData()"
value=
"load data"
/>
<input
type=
"button"
onclick=
"deleteData()"
value=
"delete data"
/>
<div
class=
"fjs-placeholder-container"
style=
"width: 2000px;"
>
<div
style=
"width: 1000px;"
>
<div
id=
"placeholder1"
></div>
</div>
<div
style=
"width: 1000px;"
>
<div
id=
"placeholder2"
></div>
</div>
</div>
</body>
<script>
/* eslint-disable */
const
fjs
=
fractal
.
init
({
handler
:
'
ada
'
,
thisBaseURL
:
'
https://ada.parkinson.lu
'
,
fractalisBaseURL
:
'
http://127.0.0.1:5000
'
,
getAuth
()
{
return
credentials1
}
})
function
loadData
()
{
fjs
.
loadData
([
{
dictionary
:
{
name
:
'
entropyR
'
,
projection
:
'
entropyR
'
,
label
:
'
Entropy Right
'
,
fieldType
:
'
Double
'
,
isArray
:
true
},
data_set
:
'
egait.features
'
},
{
dictionary
:
{
name
:
'
entropyL
'
,
projection
:
'
entropyL
'
,
label
:
'
Entropy Left
'
,
fieldType
:
'
Double
'
,
isArray
:
true
},
data_set
:
'
egait.features
'
}
])
}
function
deleteData
()
{
fjs
.
clearCache
()
}
fjs
.
setChart
({
chart
:
'
heatmap
'
,
selector
:
'
#placeholder1
'
})
</script>
<style>
.fjs-placeholder-container
{
display
:
flex
;
flex-direction
:
row
;
}
</style>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment