render.lua (7400B)
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, label) 74 local xml = load_xml(path) 75 for j=1,10 do 76 local xpos = xorig+j*0.27 77 tex.print([[\node 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*0.27 83 local radius = math.sqrt(cell) * 0.15 84 local content = string.format("%.0f", 100*cell) 85 tex.print([[\fill (]]..xpos..[[, ]]..ypos..[[) circle (]]..radius..[[);]]) 86 end 87 end 88 local ypos = -CONFUSION_Y_SPREAD*#xml.confusion.gold - 0.2 89 local bwest = xorig + 0.5*0.27 90 local beast = xorig + 10.5*0.27 91 tex.print([[\draw[decorate, decoration={brace, amplitude=5}] (]]..beast..[[, ]]..ypos..[[) -- (]]..bwest..[[, ]]..ypos..[[) node[below, midway, yshift=-1mm] {]]..label..[[};]]) 92 end 93 94 function render_confusion_legend(path) 95 local xml = load_xml(path) 96 for i, gold in ipairs(xml.confusion.gold) do 97 local frequency = string.format("%.2f", 100*gold.relation.frequency) 98 if tonumber(gold.relation.frequency) < 0.1 then 99 frequency = [[\hphantom{0}]] .. frequency 100 end 101 local label = nil 102 if gold.relation.reversed then 103 label = [[\(e_2\) ]] .. gold.relation.surfaceform .. [[ \(e_1\)]] 104 else 105 label = [[\(e_1\) ]] .. gold.relation.surfaceform .. [[ \(e_2\)]] 106 end 107 local ypos = i*-CONFUSION_Y_SPREAD 108 tex.print([[\node[anchor=west] at (0, ]]..ypos..[[) {\scriptsize{}]]..frequency..[[\% ]]..label..[[ (\wdrel{]]..gold.relation.identifier..[[})};]]) 109 end 110 end 111 112 function render.confusions(path1, label1, path2, label2, path3, label3, path4, label4) 113 tex.print([[\begin{tikzpicture}]]) 114 render_confusion(path1, -12, label1) 115 render_confusion(path2, -9, label2) 116 render_confusion(path3, -6, label3) 117 render_confusion(path4, -3, label4) 118 render_confusion_legend(path1) 119 tex.print([[\end{tikzpicture}]]) 120 end 121 122 local function degrees_table(dict) 123 local table = {} 124 for _, value in ipairs(dict) do 125 table[tonumber(value.degree)] = tonumber(value.frequency) 126 end 127 return table 128 end 129 130 local function degrees_bound(degrees, first, second) 131 local upper_bound = math.min(#degrees.indegrees.value, #degrees.outdegrees.value) 132 local max_frequency = 0 133 local min_frequency = 1 134 for degree = 1, upper_bound do 135 if first[degree] == nil or second[degree] == nil or first[degree]<1e-5 or second[degree]<1e-5 then 136 return degree - 1, min_frequency, max_frequency 137 end 138 max_frequency = math.max(max_frequency, first[degree], second[degree]) 139 min_frequency = math.min(min_frequency, first[degree], second[degree]) 140 end 141 return upper_bound, min_frequency, max_frequency 142 end 143 144 local function degrees_true_max(inds, outds) 145 local maximum = 0 146 local mtype = "in" 147 for degree, count in pairs(inds) do 148 maximum = math.max(maximum, degree) 149 end 150 local inter_max = maximum 151 for degree, count in pairs(outds) do 152 maximum = math.max(maximum, degree) 153 end 154 if maximum > inter_max then 155 mtype= "out" 156 end 157 return maximum, mtype 158 end 159 160 function render.degrees(path) 161 local xml = load_xml(path) 162 local indegrees = degrees_table(xml.degrees.indegrees.value) 163 local outdegrees = degrees_table(xml.degrees.outdegrees.value) 164 local max_degree, min_frequency, max_frequency = degrees_bound(xml.degrees, indegrees, outdegrees) 165 local min_frequency = math.pow(10, math.floor(math.log(min_frequency, 10))) 166 local max_frequency = math.pow(10, math.ceil(math.log(max_frequency, 10))) 167 local right_degree = math.pow(10, math.ceil(math.log(max_degree, 10))) 168 169 tex.print([[\begin{tikzpicture}]]) 170 tex.print([[\begin{loglogaxis}[modern, width=45mm,]]) 171 tex.print([[ legend entries={in-degree, out-degree},]]) 172 tex.print([[ legend columns=2,]]) 173 tex.print([[ legend style={at={(1,1.05)}, anchor=south east, draw=none},]]) 174 tex.print([[ xlabel={degree},]]) 175 tex.print([[ ylabel={frequency},]]) 176 tex.print([[ xmin=1,]]) 177 tex.print([[ xmax=]] .. tostring(right_degree) .. [[,]]) 178 tex.print([[ ymin=]] .. tostring(min_frequency) .. [[,]]) 179 tex.print([[ ymax=]] .. tostring(max_frequency) .. [[,]]) 180 tex.print([[ ytick={1e-1, 1e-2, 1e-3, 1e-4, 1e-5},]]) 181 tex.print([[ ] ]]) 182 tex.print([[\addplot+ [Dark2-A,]]) 183 tex.print([[ mark=none,]]) 184 tex.print([[ ] coordinates {]]) 185 for degree = 1, max_degree do 186 tex.print(("(%f, %f)"):format(degree, indegrees[degree])) 187 end 188 tex.print([[};]]) 189 tex.print([[\addplot+ [Dark2-B,]]) 190 tex.print([[ mark=none,]]) 191 tex.print([[ ] coordinates {]]) 192 for degree = 1, max_degree do 193 tex.print(("(%f, %f)"):format(degree, outdegrees[degree])) 194 end 195 tex.print([[};]]) 196 tex.print([[\end{loglogaxis}]]) 197 tex.print([[\end{tikzpicture}%]]) 198 tex.print([[\def\numberarcs{]] .. render_number(xml.degrees.m) .. [[}%]]) 199 tex.print([[\def\maxdisplayeddegree{]] .. render_number(max_degree) .. [[}%]]) 200 201 local true_max_degree, true_max_type = degrees_true_max(indegrees, outdegrees) 202 tex.print([[\def\maxdegree{]] .. render_number(true_max_degree) .. [[}%]]) 203 tex.print([[\def\maxdegreetype{]] .. true_max_type .. [[}%]]) 204 end 205 206 return render