PhD

The LaTeX sources of my Ph.D. thesis
git clone https://esimon.eu/repos/PhD.git
Log | Files | Refs | README | LICENSE

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