render.lua (7926B)
1 require("io") 2 require("math") 3 4 local xmlhandler = require("luaxml-mod-handler") 5 local xmlparser = require("luaxml-mod-xml") 6 7 local CONFUSION_Y_SPREAD = 0.35 8 9 local render = {} 10 11 local function load_xml(path) 12 local treehandler = xmlhandler.simpleTreeHandler() 13 local xml = xmlparser.xmlParser(treehandler) 14 15 local file = io.open(path, "r") 16 xml:parse(file:read("*a")) 17 file:close() 18 19 return treehandler.root 20 end 21 22 local function render_number(x) 23 local s = tostring(x) 24 local r = "" 25 local c = 0 26 for i=#s,1,-1 do 27 if (#s-i) % 3 == 0 and i~=#s then 28 r = "\\," .. r 29 end 30 r = s:sub(i, i) .. r 31 end 32 return r 33 end 34 35 local function embeddings_bounds(list, field) 36 local values = {} 37 for _, item in ipairs(list) do 38 table.insert(values, tonumber(item[field])) 39 end 40 local low = math.floor(math.min(unpack(values))*2)/2 41 local high = math.ceil(math.max(unpack(values))*2)/2 42 return low, high 43 end 44 45 function render.embeddings(path) 46 local xml = load_xml(path) 47 local xmin, xmax = embeddings_bounds(xml.embeddings.embedding, "x") 48 local ymin, ymax = embeddings_bounds(xml.embeddings.embedding, "y") 49 tex.print([[\begin{tikzpicture}]]) 50 tex.print([[\begin{axis}[modern, width=50mm,]]) 51 tex.print([[ xmin=]] .. tostring(xmin) .. [[,]]) 52 tex.print([[ xmax=]] .. tostring(xmax) .. [[,]]) 53 tex.print([[ ymin=]] .. tostring(ymin) .. [[,]]) 54 tex.print([[ ymax=]] .. tostring(ymax) .. [[,]]) 55 tex.print([[ xtick={]] .. tostring(xmin) .. [[,]] .. tostring(xmin+0.5) .. [[,...,]] .. tostring(xmax) .. [[},]]) 56 tex.print([[ ytick={]] .. tostring(ymin) .. [[,]] .. tostring(ymin+0.5) .. [[,...,]] .. tostring(ymax) .. [[},]]) 57 tex.print([[ x tick label style={rotate=90,anchor=east},]]) 58 tex.print([[ ] ]]) 59 tex.print([[\addplot+ [black, only marks, mark=*, mark options={fill=black}, nodes near coords, point meta=explicit symbolic,]]) 60 tex.print([[ coordinate style/.condition={x<0}{anchor=west},]]) 61 tex.print([[ coordinate style/.condition={x>0}{anchor=east},]]) 62 tex.print([[ ] coordinates {]]) 63 for _, embedding in ipairs(xml.embeddings.embedding) do 64 tex.print(("(%f, %f) [%s]"):format(embedding.x, embedding.y, embedding.label)) 65 end 66 tex.print([[};]]) 67 tex.print([[\end{axis}]]) 68 tex.print([[\end{tikzpicture}%]]) 69 tex.print([[\def\explainedvarx{]] .. ([[%2.1f\%%]]):format(100*xml.embeddings.explained.x) .. [[}%]]) 70 tex.print([[\def\explainedvary{]] .. ([[%2.1f\%%]]):format(100*xml.embeddings.explained.y) .. [[}%]]) 71 end 72 73 function render_confusion(path, xorig, xdelta, radius, label, id) 74 local xml = load_xml(path) 75 for j=1,10 do 76 local xpos = xorig+j*xdelta 77 tex.print([[\node (confusion-column-]]..id..[[-]]..j..[[) at (]]..xpos..[[, 0) {\scriptsize ]]..(j-1)..[[};]]) 78 end 79 for i, gold in ipairs(xml.confusion.gold) do 80 local ypos = i*-CONFUSION_Y_SPREAD 81 for j, cell in ipairs(gold.clusters.recall) do 82 local xpos = xorig+j*xdelta 83 local radius = math.sqrt(cell) * radius 84 local content = string.format("%.0f", 100*cell) 85 tex.print([[\fill (]]..xpos..[[, ]]..ypos..[[) circle (]]..radius..[[);]]) 86 tex.print([[\coordinate (confusion-cell-]]..id..[[-]]..i..[[-]]..j..[[) at (]]..xpos..[[, ]]..ypos..[[);]]) 87 end 88 end 89 local ypos = -CONFUSION_Y_SPREAD*#xml.confusion.gold - 0.2 90 local bwest = xorig + 0.5*xdelta 91 local beast = xorig + 10.5*xdelta 92 tex.print([[\draw[decorate, decoration={brace, amplitude=5}] (]]..beast..[[, ]]..ypos..[[) -- (]]..bwest..[[, ]]..ypos..[[) node[below, midway, yshift=-1mm] (confusion-model-]]..id..[[) {]]..label..[[};]]) 93 end 94 95 function render_confusion_legend(path) 96 local xml = load_xml(path) 97 for i, gold in ipairs(xml.confusion.gold) do 98 local frequency = string.format("%.2f", 100*gold.relation.frequency) 99 if tonumber(gold.relation.frequency) < 0.1 then 100 frequency = [[\hphantom{0}]] .. frequency 101 end 102 local label = nil 103 if gold.relation.reversed then 104 label = [[\(e_2\) ]] .. gold.relation.surfaceform .. [[ \(e_1\)]] 105 else 106 label = [[\(e_1\) ]] .. gold.relation.surfaceform .. [[ \(e_2\)]] 107 end 108 local ypos = i*-CONFUSION_Y_SPREAD 109 tex.print([[\node[anchor=west] (confusion-row-]]..i..[[) at (0, ]]..ypos..[[) {\makeatletter\scriptsize{}\ifthesis@presentation\else]]..frequency..[[\% \fi]]..label..[[\ifthesis@presentation\else (\wdrel{]]..gold.relation.identifier..[[})\fi\makeatother};]]) 110 end 111 end 112 113 function render.confusions(mdelta, cdelta, radius, path1, label1, path2, label2, path3, label3, path4, label4, intikzpicture) 114 if not intikzpicture then 115 tex.print([[\begin{tikzpicture}]]) 116 end 117 render_confusion(path1, -4*mdelta, cdelta, radius, label1, 1) 118 render_confusion(path2, -3*mdelta, cdelta, radius, label2, 2) 119 render_confusion(path3, -2*mdelta, cdelta, radius, label3, 3) 120 render_confusion(path4, -1*mdelta, cdelta, radius, label4, 4) 121 render_confusion_legend(path1) 122 if not intikzpicture then 123 tex.print([[\end{tikzpicture}]]) 124 end 125 end 126 127 local function degrees_table(dict) 128 local table = {} 129 for _, value in ipairs(dict) do 130 table[tonumber(value.degree)] = tonumber(value.frequency) 131 end 132 return table 133 end 134 135 local function degrees_bound(degrees, first, second) 136 local upper_bound = math.min(#degrees.indegrees.value, #degrees.outdegrees.value) 137 local max_frequency = 0 138 local min_frequency = 1 139 for degree = 1, upper_bound do 140 if first[degree] == nil or second[degree] == nil or first[degree]<1e-5 or second[degree]<1e-5 then 141 return degree - 1, min_frequency, max_frequency 142 end 143 max_frequency = math.max(max_frequency, first[degree], second[degree]) 144 min_frequency = math.min(min_frequency, first[degree], second[degree]) 145 end 146 return upper_bound, min_frequency, max_frequency 147 end 148 149 local function degrees_true_max(inds, outds) 150 local maximum = 0 151 local mtype = "in" 152 for degree, count in pairs(inds) do 153 maximum = math.max(maximum, degree) 154 end 155 local inter_max = maximum 156 for degree, count in pairs(outds) do 157 maximum = math.max(maximum, degree) 158 end 159 if maximum > inter_max then 160 mtype= "out" 161 end 162 return maximum, mtype 163 end 164 165 function render.degrees(path) 166 local xml = load_xml(path) 167 local indegrees = degrees_table(xml.degrees.indegrees.value) 168 local outdegrees = degrees_table(xml.degrees.outdegrees.value) 169 local max_degree, min_frequency, max_frequency = degrees_bound(xml.degrees, indegrees, outdegrees) 170 local min_frequency = math.pow(10, math.floor(math.log(min_frequency, 10))) 171 local max_frequency = math.pow(10, math.ceil(math.log(max_frequency, 10))) 172 local right_degree = math.pow(10, math.ceil(math.log(max_degree, 10))) 173 174 tex.print([[\begin{tikzpicture}]]) 175 tex.print([[\begin{loglogaxis}[modern, width=45mm,]]) 176 tex.print([[ legend entries={in-degree, out-degree},]]) 177 tex.print([[ legend columns=2,]]) 178 tex.print([[ legend style={at={(1,1.05)}, anchor=south east, draw=none},]]) 179 tex.print([[ xlabel={degree},]]) 180 tex.print([[ ylabel={frequency},]]) 181 tex.print([[ xmin=1,]]) 182 tex.print([[ xmax=]] .. tostring(right_degree) .. [[,]]) 183 tex.print([[ ymin=]] .. tostring(min_frequency) .. [[,]]) 184 tex.print([[ ymax=]] .. tostring(max_frequency) .. [[,]]) 185 tex.print([[ ytick={1e-1, 1e-2, 1e-3, 1e-4, 1e-5},]]) 186 tex.print([[ ] ]]) 187 tex.print([[\addplot+ [Dark2-A,]]) 188 tex.print([[ mark=none,]]) 189 tex.print([[ ] coordinates {]]) 190 for degree = 1, max_degree do 191 tex.print(("(%f, %f)"):format(degree, indegrees[degree])) 192 end 193 tex.print([[};]]) 194 tex.print([[\addplot+ [Dark2-B,]]) 195 tex.print([[ mark=none,]]) 196 tex.print([[ ] coordinates {]]) 197 for degree = 1, max_degree do 198 tex.print(("(%f, %f)"):format(degree, outdegrees[degree])) 199 end 200 tex.print([[};]]) 201 tex.print([[\end{loglogaxis}]]) 202 tex.print([[\end{tikzpicture}%]]) 203 tex.print([[\def\numberarcs{]] .. render_number(xml.degrees.m) .. [[}%]]) 204 tex.print([[\def\maxdisplayeddegree{]] .. render_number(max_degree) .. [[}%]]) 205 206 local true_max_degree, true_max_type = degrees_true_max(indegrees, outdegrees) 207 tex.print([[\def\maxdegree{]] .. render_number(true_max_degree) .. [[}%]]) 208 tex.print([[\def\maxdegreetype{]] .. true_max_type .. [[}%]]) 209 end 210 211 return render