work on result rendering

* add tracks + heat map
* copy result lists instead of using just a reference
* introduce proper logging
simu_flags
agp8x 2017-08-16 15:26:44 +02:00
parent b81719aba3
commit 36ff8fd0be
12 changed files with 249 additions and 28 deletions

View File

@ -1,15 +1,15 @@
from typing import List from typing import List
from .analyzer import Analyzer, Result from .analyzer import Analyzer, Result
from .analyzer.biogames import BoardDurationAnalyzer, SimulationRoundsAnalyzer from .analyzer.biogames import BoardDurationAnalyzer, SimulationRoundsAnalyzer, ActivationSequenceAnalyzer
from .analyzer.default import LogEntryCountAnalyzer, LocationAnalyzer, LogEntrySequenceAnalyzer, ActionSequenceAnalyzer from .analyzer.default import LogEntryCountAnalyzer, LocationAnalyzer, LogEntrySequenceAnalyzer, ActionSequenceAnalyzer
from .analyzer.locomotion import LocomotionActionAnalyzer, CacheSequenceAnalyzer from .analyzer.locomotion import LocomotionActionAnalyzer, CacheSequenceAnalyzer
from .analyzer.mask import MaskSpatials from .analyzer.mask import MaskSpatials
from .render import Render from .render import Render
from .render.default import PrintRender, JSONRender from .render.default import PrintRender, JSONRender, TrackRender, HeatMapRender
from .render.locomotion import LocomotionActionRelativeRender, LocomotionActionAbsoluteRender, \ from .render.locomotion import LocomotionActionRelativeRender, LocomotionActionAbsoluteRender, \
LocomotionActionRatioRender LocomotionActionRatioRender
from .render.biogames import SimulationRoundsRender from .render.biogames import SimulationRoundsRender, BoardDurationHistRender, BoardDurationBoxRender
__FALLBACK__ = PrintRender __FALLBACK__ = PrintRender
__MAPPING__ = { __MAPPING__ = {
@ -23,7 +23,18 @@ __MAPPING__ = {
SimulationRoundsAnalyzer: [ SimulationRoundsAnalyzer: [
JSONRender, JSONRender,
SimulationRoundsRender, SimulationRoundsRender,
] ],
BoardDurationAnalyzer: [
BoardDurationHistRender,
BoardDurationBoxRender,
],
ActivationSequenceAnalyzer: [
JSONRender,
],
LocationAnalyzer: [
TrackRender,
#HeatMapRender,
],
} }
@ -37,4 +48,6 @@ def render(cls: type, results: List[Result]):
for r in get_renderer(cls): for r in get_renderer(cls):
p = r() p = r()
p.result_types.append(cls) p.result_types.append(cls)
p.render(results) rendered = p.render(results)
if rendered:
print(str(r))

View File

@ -1,19 +1,24 @@
from analyzers.settings import LogSettings from analyzers.settings import LogSettings
import logging
log = logging.getLogger(__name__)
class Result: class Result:
def __init__(self, analysis, result): def __init__(self, analysis, result):
self.result = result self.result = result
self.__analysis__ = analysis self.__analysis__ = analysis
log.debug("set" + str(len(self.result)))
def analysis(self): def analysis(self):
return self.__analysis__ return self.__analysis__
def get(self): def get(self):
log.debug("get" + str(len(self.result)))
return self.result return self.result
def __repr__(self): def __repr__(self):
return "<Result " + str(self.__analysis__) + ": " + str(type(self.result)) + ">" return "<Result " + str(self.__analysis__) + ": " + str(type(self.result)) + " "+str(len(self.result))+">"
class Analyzer: class Analyzer:

View File

@ -1,7 +1,10 @@
import logging
from collections import defaultdict from collections import defaultdict
from . import Result, LogSettings, Analyzer from . import Result, LogSettings, Analyzer
logger = logging.getLogger(__name__)
class BoardDurationAnalyzer(Analyzer): class BoardDurationAnalyzer(Analyzer):
""" """
@ -61,3 +64,21 @@ class SimulationRoundsAnalyzer(Analyzer):
simu_id = entry['answers']["@id"] simu_id = entry['answers']["@id"]
self.store[simu_id] += 1 self.store[simu_id] += 1
return False return False
class ActivationSequenceAnalyzer(Analyzer):
__name__ = "ActivationSequence"
def __init__(self, settings: LogSettings):
super().__init__(settings)
self.store = []
def result(self) -> Result:
return Result(type(self), self.store)
def process(self, entry: dict) -> bool:
if entry[self.settings.type_field] in self.settings.sequences['start']:
if entry['cache']:
self.store.append(entry['cache']['@id'])
else:
logger.error("null cache")

View File

@ -1,4 +1,4 @@
import json import logging
from collections import defaultdict from collections import defaultdict
from . import Result, LogSettings, Analyzer from . import Result, LogSettings, Analyzer
@ -10,25 +10,19 @@ class LocationAnalyzer(Analyzer):
""" """
entries = [] entries = []
__name__ = "Location" __name__ = "Location"
log = logging.getLogger(__name__)
class Formats:
geojson = 0
def __init__(self, settings: LogSettings): def __init__(self, settings: LogSettings):
super().__init__(settings) super().__init__(settings)
def result(self) -> Result: def result(self) -> Result:
# return self.entries self.log.debug(len(self.entries))
return Result(type(self), self.entries) return Result(type(self), list(self.entries))
def render(self, format: int = Formats.geojson):
if format is self.Formats.geojson:
return json.dumps([entry['location']['coordinates'] for entry in self.entries])
raise NotImplementedError()
def process(self, entry: dict) -> bool: def process(self, entry: dict) -> bool:
if entry[self.settings.type_field] in self.settings.spatials: if entry[self.settings.type_field] in self.settings.spatials:
self.entries.append(entry) self.entries.append(entry)
#self.log.debug(len(self.entries))
return False return False
@ -57,7 +51,7 @@ class LogEntrySequenceAnalyzer(Analyzer):
__name__ = "LogEntrySequence" __name__ = "LogEntrySequence"
def result(self) -> Result: def result(self) -> Result:
return Result(type(self), self.store) return Result(type(self), list(self.store))
def process(self, entry: dict) -> bool: def process(self, entry: dict) -> bool:
entry_type = entry[self.settings.type_field] entry_type = entry[self.settings.type_field]

View File

@ -87,7 +87,7 @@ class CacheSequenceAnalyzer(Analyzer):
return False return False
def result(self) -> Result: def result(self) -> Result:
return Result(type(self), self.store) return Result(type(self), list(self.store))
def __init__(self, settings: LogSettings): def __init__(self, settings: LogSettings):
super().__init__(settings) super().__init__(settings)

View File

@ -4,7 +4,7 @@ from typing import List, Tuple
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from . import Render from . import Render
from .. import Result, SimulationRoundsAnalyzer from .. import Result, SimulationRoundsAnalyzer, BoardDurationAnalyzer
def plot(src_data: List[Tuple[str, List[int]]]): def plot(src_data: List[Tuple[str, List[int]]]):
@ -29,3 +29,36 @@ class SimulationRoundsRender(Render):
plot(data_tuples) plot(data_tuples)
result_types = [SimulationRoundsAnalyzer] result_types = [SimulationRoundsAnalyzer]
class BoardDurationHistRender(Render):
result_types = [BoardDurationAnalyzer]
def render(self, results: List[Result]):
data = []
for result in self.filter(results):
session = result.get()
_data = []
for board in session:
if "active" in board:
_data.append(board["active"])
else:
_data.append(0)
data.append(_data)
n, bins, patches = plt.hist(data, log=True)
plt.show()
class BoardDurationBoxRender(Render):
result_types = [BoardDurationAnalyzer]
def render(self, results: List[Result]):
data = defaultdict(list)
for result in self.filter(results):
get = result.get()
for board in get:
duration = board['active'] if 'active' in board else 0
data[board['id']].append(duration)
data_tuples = [(key, data[key]) for key in sorted(data)]
data_tuples = sorted(data_tuples, key=lambda x: sum(x[1]))
plot(data_tuples)

View File

@ -1,7 +1,11 @@
import json import json
import logging
from typing import List from typing import List
from . import Render, Result from . import Render, Result
from .. import LocationAnalyzer
log = logging.getLogger(__name__)
class PrintRender(Render): class PrintRender(Render):
@ -12,3 +16,34 @@ class PrintRender(Render):
class JSONRender(Render): class JSONRender(Render):
def render(self, results: List[Result]): def render(self, results: List[Result]):
print(json.dumps([r.get() for r in self.filter(results)], indent=1)) print(json.dumps([r.get() for r in self.filter(results)], indent=1))
class TrackRender(Render):
result_types = [LocationAnalyzer]
def render(self, results: List[Result]):
data = []
log.debug(results)
for result in self.filter(results):
if len(result.get()) > 0:
data.append(
[[entry['location']['coordinates'][1], entry['location']['coordinates'][0]] for entry in
result.get()])
dumps = json.dumps(data)
with open("track_data.js", "w") as out:
out.write("tracks=" + dumps + ";")
return dumps
class HeatMapRender(TrackRender):
weight = 0.01
def render(self, results: List[Result]):
raw = super(HeatMapRender, self).render(results)
data = []
for session in json.loads(raw):
data += [(entry[0], entry[1], self.weight) for entry in session]
dumps = json.dumps(data)
with open('heat_data.js', 'w') as out:
out.write("coords = " + dumps + ";")
return dumps

View File

@ -13,16 +13,18 @@
], ],
"analyzers": { "analyzers": {
"analyzers": [ "analyzers": [
"LocationAnalyzer", "LocationAnalyzer"
]
},
"disabled_analyzers":[
"LogEntryCountAnalyzer", "LogEntryCountAnalyzer",
"LogEntrySequenceAnalyzer", "LogEntrySequenceAnalyzer",
"ActionSequenceAnalyzer", "ActionSequenceAnalyzer",
"BoardDurationAnalyzer", "BoardDurationAnalyzer",
"LocomotionActionAnalyzer", "LocomotionActionAnalyzer",
"CacheSequenceAnalyzer", "CacheSequenceAnalyzer",
"SimulationRoundsAnalyzer" "SimulationRoundsAnalyzer",
] "ActivationSequenceAnalyzer"],
},
"sequences": { "sequences": {
"start": "de.findevielfalt.games.game2.instance.log.entry.LogEntryCache", "start": "de.findevielfalt.games.game2.instance.log.entry.LogEntryCache",
"end": { "end": {

View File

@ -1,3 +1,4 @@
import logging
from load import LOADERS from load import LOADERS
from typing import List from typing import List
from analyzers import get_renderer, Analyzer, render from analyzers import get_renderer, Analyzer, render
@ -5,6 +6,10 @@ from analyzers.settings import LogSettings, load_settings
import analyzers import analyzers
logging.basicConfig(format='%(levelname)s %(name)s:%(message)s', level=logging.DEBUG)
logger = logging.getLogger(__name__)
def process_log(log_id: str, settings: LogSettings) -> List[Analyzer]: def process_log(log_id: str, settings: LogSettings) -> List[Analyzer]:
logfile = "data/inst_{id}.{format}".format(id=log_id, format=settings.log_format) logfile = "data/inst_{id}.{format}".format(id=log_id, format=settings.log_format)
loader = LOADERS[settings.log_format]() loader = LOADERS[settings.log_format]()
@ -28,7 +33,8 @@ if __name__ == '__main__':
"20d4244719404ffab0ca386c76e4b112", "20d4244719404ffab0ca386c76e4b112",
"56d9b64144ab44e7b90bf766f3be32e3", "56d9b64144ab44e7b90bf766f3be32e3",
"dc2cdc28ca074715b905e4aa5badff10", "dc2cdc28ca074715b905e4aa5badff10",
"e32b16998440475b994ab46d481d3e0c", ] "e32b16998440475b994ab46d481d3e0c",
]
log_ids = [ log_ids = [
"34fecf49dbaca3401d745fb467", "34fecf49dbaca3401d745fb467",
"44ea194de594cd8d63ac0314be", "44ea194de594cd8d63ac0314be",
@ -41,16 +47,17 @@ if __name__ == '__main__':
"fe1331481f85560681f86827ec", "fe1331481f85560681f86827ec",
"fec57041458e6cef98652df625", ] "fec57041458e6cef98652df625", ]
results = [] results = []
#TODO: capture session ID, dict
for log_id in log_ids: for log_id in log_ids:
for analysis in process_log(log_id, settings): for analysis in process_log(log_id, settings):
print("* Result for " + analysis.name()) logger.info("* Result for " + analysis.name())
# print(analysis.result()) # print(analysis.result())
# print(analysis.render()) # print(analysis.render())
results.append(analysis.result()) results.append(analysis.result())
if False: if False:
for r in get_renderer(analyzers.LocomotionActionAnalyzer): for r in get_renderer(analyzers.LocomotionActionAnalyzer):
r().render(results) r().render(results)
render(analyzers.SimulationRoundsAnalyzer, results) render(analyzers.LocationAnalyzer, results)
# for analyzers in analyzers: # for analyzers in analyzers:
# if analyzers.name() in ["LogEntryCount", "ActionSequenceAnalyzer"]: # if analyzers.name() in ["LogEntryCount", "ActionSequenceAnalyzer"]:

View File

@ -1 +0,0 @@
[]

46
static/heatmap.html Normal file
View File

@ -0,0 +1,46 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet.heat demo</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.js"></script>
<style>
#map { width: 1024px; height: 768px; }
body { font: 16px/1.4 "Helvetica Neue", Arial, sans-serif; }
.ghbtns { position: relative; top: 4px; margin-left: 5px; }
a { color: #0077ff; }
</style>
</head>
<body>
<div id="map"></div>
<!-- <script src="../node_modules/simpleheat/simpleheat.js"></script>
<script src="../src/HeatLayer.js"></script> -->
<script src="https://rawgit.com/Leaflet/Leaflet.heat/gh-pages/dist/leaflet-heat.js"></script>
<!--script src="./test.js"></script-->
<script>
var options = {maxZoom:22};
var map = L.map('map', options).setView([49.90299388, 10.87004638], 17);
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
}).addTo(map);
function addHeat(coords){
var transformed = coords.map(function (p) { return [p[1], p[0], 0.25]; });
var heat = L.heatLayer(transformed).addTo(map);
}
//coords = coords.map(function (p) { return [p[1], p[0], 0.05]; });
//var heat = L.heatLayer(coords).addTo(map);
addHeat(coords);
</script>
<!--script src="./coord.js"></script>
<script>
//addHeat(coords);
</script-->
</body>
</html>

66
static/trackmap.html Normal file
View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet.heat demo</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.2.0/dist/leaflet.css"
integrity="sha512-M2wvCLH6DSRazYeZRIm1JnYyh22purTM+FDB5CsyxtQJYeKq83arPe5wgbNmcFXGqiSH2XR8dT/fJISVA1r/zQ=="
crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.2.0/dist/leaflet.js"
integrity="sha512-lInM/apFSqyy1o6s89K4iQUKg6ppXEgsVxT35HbzUupEVRh2Eu9Wdl4tHj7dZO0s1uvplcYGmt3498TtHq+log=="
crossorigin=""></script>
<style>
#map { width: 1024px; height: 768px; }
body { font: 16px/1.4 "Helvetica Neue", Arial, sans-serif; }
.ghbtns { position: relative; top: 4px; margin-left: 5px; }
a { color: #0077ff; }
</style>
</head>
<body>
<div id="map"></div>
<!-- <script src="../node_modules/simpleheat/simpleheat.js"></script>
<script src="../src/HeatLayer.js"></script> -->
<script src="http://rawgit.com/Leaflet/Leaflet.heat/gh-pages/dist/leaflet-heat.js"></script>
<script src="./track_data.js"></script>
<script>
var options = {maxZoom:22};
//var map = L.map('map', options).setView([49.90299388, 10.87004638], 17);
var map = L.map('map', options);
var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
}).addTo(map);
function addHeat(coords){
//var transformed = coords.map(function (p) { return [p[1], p[0], 0.25]; });
var heat = L.heatLayer(coords).addTo(map);
}
var layers=[];
function addTrack(tracks, i){
layers[i] = L.polyline(tracks[i], {color:"green"});
map.fitBounds(layers[i].getBounds());
layers[i].on('mouseover', function (e) {
e.target.setStyle({'color':'red'});
});
layers[i].on('mouseout', function (e) {
e.target.setStyle({'color':'green'});
});
}
//coords = coords.map(function (p) { return [p[1], p[0], 0.05]; });
//var heat = L.heatLayer(coords).addTo(map);
//addHeat(coords);
for (var i in tracks) {
addTrack(tracks, i);
}
L.control.layers(null, layers).addTo(map);
</script>
<!--script src="./heat_data.js"></script>
<script>
addHeat(coords);
</script-->
</body>
</html>