\documentclass[10pt,a4paper]{article} % Packages \usepackage{fancyhdr} % For header and footer \usepackage{multicol} % Allows multicols in tables \usepackage{tabularx} % Intelligent column widths \usepackage{tabulary} % Used in header and footer \usepackage{hhline} % Border under tables \usepackage{graphicx} % For images \usepackage{xcolor} % For hex colours %\usepackage[utf8x]{inputenc} % For unicode character support \usepackage[T1]{fontenc} % Without this we get weird character replacements \usepackage{colortbl} % For coloured tables \usepackage{setspace} % For line height \usepackage{lastpage} % Needed for total page number \usepackage{seqsplit} % Splits long words. %\usepackage{opensans} % Can't make this work so far. Shame. Would be lovely. \usepackage[normalem]{ulem} % For underlining links % Most of the following are not required for the majority % of cheat sheets but are needed for some symbol support. \usepackage{amsmath} % Symbols \usepackage{MnSymbol} % Symbols \usepackage{wasysym} % Symbols %\usepackage[english,german,french,spanish,italian]{babel} % Languages % Document Info \author{RJ Murray (murenei)} \pdfinfo{ /Title (network-analysis-with-python-and-networkx.pdf) /Creator (Cheatography) /Author (RJ Murray (murenei)) /Subject (Network Analysis with Python and NetworkX Cheat Sheet) } % Lengths and widths \addtolength{\textwidth}{6cm} \addtolength{\textheight}{-1cm} \addtolength{\hoffset}{-3cm} \addtolength{\voffset}{-2cm} \setlength{\tabcolsep}{0.2cm} % Space between columns \setlength{\headsep}{-12pt} % Reduce space between header and content \setlength{\headheight}{85pt} % If less, LaTeX automatically increases it \renewcommand{\footrulewidth}{0pt} % Remove footer line \renewcommand{\headrulewidth}{0pt} % Remove header line \renewcommand{\seqinsert}{\ifmmode\allowbreak\else\-\fi} % Hyphens in seqsplit % This two commands together give roughly % the right line height in the tables \renewcommand{\arraystretch}{1.3} \onehalfspacing % Commands \newcommand{\SetRowColor}[1]{\noalign{\gdef\RowColorName{#1}}\rowcolor{\RowColorName}} % Shortcut for row colour \newcommand{\mymulticolumn}[3]{\multicolumn{#1}{>{\columncolor{\RowColorName}}#2}{#3}} % For coloured multi-cols \newcolumntype{x}[1]{>{\raggedright}p{#1}} % New column types for ragged-right paragraph columns \newcommand{\tn}{\tabularnewline} % Required as custom column type in use % Font and Colours \definecolor{HeadBackground}{HTML}{333333} \definecolor{FootBackground}{HTML}{666666} \definecolor{TextColor}{HTML}{333333} \definecolor{DarkBackground}{HTML}{439DA3} \definecolor{LightBackground}{HTML}{F3F8F9} \renewcommand{\familydefault}{\sfdefault} \color{TextColor} % Header and Footer \pagestyle{fancy} \fancyhead{} % Set header to blank \fancyfoot{} % Set footer to blank \fancyhead[L]{ \noindent \begin{multicols}{3} \begin{tabulary}{5.8cm}{C} \SetRowColor{DarkBackground} \vspace{-7pt} {\parbox{\dimexpr\textwidth-2\fboxsep\relax}{\noindent \hspace*{-6pt}\includegraphics[width=5.8cm]{/web/www.cheatography.com/public/images/cheatography_logo.pdf}} } \end{tabulary} \columnbreak \begin{tabulary}{11cm}{L} \vspace{-2pt}\large{\bf{\textcolor{DarkBackground}{\textrm{Network Analysis with Python and NetworkX Cheat Sheet}}}} \\ \normalsize{by \textcolor{DarkBackground}{RJ Murray (murenei)} via \textcolor{DarkBackground}{\uline{cheatography.com/58736/cs/15946/}}} \end{tabulary} \end{multicols}} \fancyfoot[L]{ \footnotesize \noindent \begin{multicols}{3} \begin{tabulary}{5.8cm}{LL} \SetRowColor{FootBackground} \mymulticolumn{2}{p{5.377cm}}{\bf\textcolor{white}{Cheatographer}} \\ \vspace{-2pt}RJ Murray (murenei) \\ \uline{cheatography.com/murenei} \\ \uline{\seqsplit{tutify}.com.au} \end{tabulary} \vfill \columnbreak \begin{tabulary}{5.8cm}{L} \SetRowColor{FootBackground} \mymulticolumn{1}{p{5.377cm}}{\bf\textcolor{white}{Cheat Sheet}} \\ \vspace{-2pt}Published 4th June, 2018.\\ Updated 4th June, 2018.\\ Page {\thepage} of \pageref{LastPage}. \end{tabulary} \vfill \columnbreak \begin{tabulary}{5.8cm}{L} \SetRowColor{FootBackground} \mymulticolumn{1}{p{5.377cm}}{\bf\textcolor{white}{Sponsor}} \\ \SetRowColor{white} \vspace{-5pt} %\includegraphics[width=48px,height=48px]{dave.jpeg} Measure your website readability!\\ www.readability-score.com \end{tabulary} \end{multicols}} \begin{document} \raggedright \raggedcolumns % Set font size to small. Switch to any value % from this page to resize cheat sheet text: % www.emerson.emory.edu/services/latex/latex_169.html \footnotesize % Small font. \begin{multicols*}{2} \begin{tabularx}{8.4cm}{x{4 cm} x{4 cm} } \SetRowColor{DarkBackground} \mymulticolumn{2}{x{8.4cm}}{\bf\textcolor{white}{Basic graph manipulation}} \tn % Row 0 \SetRowColor{LightBackground} \mymulticolumn{2}{x{8.4cm}}{`import networkx as nx`} \tn % Row Count 1 (+ 1) % Row 1 \SetRowColor{white} \mymulticolumn{2}{x{8.4cm}}{`G=nx.Graph()`} \tn % Row Count 2 (+ 1) % Row 2 \SetRowColor{LightBackground} `G=nx.MultiGraph()` & Create a graph allowing parallel edges \tn % Row Count 4 (+ 2) % Row 3 \SetRowColor{white} `G.add\_edges\_from({[}(0, 1),(0, 2),(1, 3),(2, 4){]}` & Create graph from edges \tn % Row Count 7 (+ 3) % Row 4 \SetRowColor{LightBackground} \seqsplit{`nx.draw\_networkx(G)`} & Draw the graph \tn % Row Count 9 (+ 2) % Row 5 \SetRowColor{white} `G.add\_node('A',role='manager')` & Add a node \tn % Row Count 11 (+ 2) % Row 6 \SetRowColor{LightBackground} `G.add\_edge('A','B',relation = 'friend')` & Add an edge \tn % Row Count 14 (+ 3) % Row 7 \SetRowColor{white} `G.node{[}'A'{]}{[}'role'{]} = 'team member'` & Set attribute of a node \tn % Row Count 16 (+ 2) % Row 8 \SetRowColor{LightBackground} `G.node{[}'A'{]}`, `G.edge{[}('A','B'){]}` & View attributes of node, edge \tn % Row Count 18 (+ 2) % Row 9 \SetRowColor{white} `G.edges()`, `G.nodes()` & Show edges, nodes \tn % Row Count 20 (+ 2) % Row 10 \SetRowColor{LightBackground} `list(G.edges())` & Return as list instead of EdgeView class \tn % Row Count 22 (+ 2) % Row 11 \SetRowColor{white} \seqsplit{`G.nodes(data=True)`}, \seqsplit{`G.edges(data=True)`} & Include node/edge attributes \tn % Row Count 25 (+ 3) % Row 12 \SetRowColor{LightBackground} \seqsplit{`G.nodes(data='relation)`} & Return specific attribute \tn % Row Count 27 (+ 2) \hhline{>{\arrayrulecolor{DarkBackground}}--} \end{tabularx} \par\addvspace{1.3em} \begin{tabularx}{8.4cm}{x{4.64 cm} x{3.36 cm} } \SetRowColor{DarkBackground} \mymulticolumn{2}{x{8.4cm}}{\bf\textcolor{white}{Creating graphs from data}} \tn % Row 0 \SetRowColor{LightBackground} \seqsplit{`G=nx.read\_adjlist('G\_adjlist.txt'}, nodetype=int)` & Create from adjacency list \tn % Row Count 3 (+ 3) % Row 1 \SetRowColor{white} `G=nx.Graph(G\_mat)` & Create from matrix (np.array) \tn % Row Count 5 (+ 2) % Row 2 \SetRowColor{LightBackground} \seqsplit{`G=nx.read\_edgelist('G\_edgelist.txt'}, data={[}('Weight', int){]})` & Create from edgelist \tn % Row Count 8 (+ 3) % Row 3 \SetRowColor{white} \seqsplit{`G=nx.from\_pandas\_dataframe(G\_df}, 'n1', 'n2', edge\_attr='weight')` & Create from df \tn % Row Count 11 (+ 3) \hhline{>{\arrayrulecolor{DarkBackground}}--} \SetRowColor{LightBackground} \mymulticolumn{2}{x{8.4cm}}{Adjacency list format \newline 0 1 2 3 5 \newline 1 3 6 ... \newline \newline Edgelist format: \newline 0 1 14 \newline 0 2 17} \tn \hhline{>{\arrayrulecolor{DarkBackground}}--} \end{tabularx} \par\addvspace{1.3em} \begin{tabularx}{8.4cm}{x{4 cm} x{4 cm} } \SetRowColor{DarkBackground} \mymulticolumn{2}{x{8.4cm}}{\bf\textcolor{white}{Bipartite graphs}} \tn % Row 0 \SetRowColor{LightBackground} \mymulticolumn{2}{x{8.4cm}}{`from networkx.algorithms import bipartite`} \tn % Row Count 1 (+ 1) % Row 1 \SetRowColor{white} \seqsplit{`bipartite.is\_bipartite(B)`} & Check if graph B is bipartite \tn % Row Count 3 (+ 2) % Row 2 \SetRowColor{LightBackground} \seqsplit{`bipartite.is\_bipartite\_node\_set(B},set)` & Check if set of nodes is bipartition of graph \tn % Row Count 6 (+ 3) % Row 3 \SetRowColor{white} `bipartite.sets(B)` & Get each set of nodes of bipartite graph \tn % Row Count 8 (+ 2) % Row 4 \SetRowColor{LightBackground} \seqsplit{`bipartite.projected\_graph(B}, X)` & Bipartite projected graph - nodes with bipartite friends in common \tn % Row Count 12 (+ 4) % Row 5 \SetRowColor{white} \seqsplit{`P=bipartite.weighted\_projected\_graph(B}, X)` & projected graph with weights (number of friends in common) \tn % Row Count 15 (+ 3) \hhline{>{\arrayrulecolor{DarkBackground}}--} \end{tabularx} \par\addvspace{1.3em} \begin{tabularx}{8.4cm}{x{3.76 cm} x{4.24 cm} } \SetRowColor{DarkBackground} \mymulticolumn{2}{x{8.4cm}}{\bf\textcolor{white}{Network Connectivity}} \tn % Row 0 \SetRowColor{LightBackground} `nx.clustering(G, node)` & Local clustering coefficient \tn % Row Count 2 (+ 2) % Row 1 \SetRowColor{white} \seqsplit{`nx.average\_clustering(G)`} & Global clustering coefficient \tn % Row Count 4 (+ 2) % Row 2 \SetRowColor{LightBackground} \seqsplit{`nx.transitivity(G)`} & Transitivity (\% of open triads) \tn % Row Count 6 (+ 2) % Row 3 \SetRowColor{white} \seqsplit{`nx.shortest\_path(G},n1,n2)` & Outputs the path itself \tn % Row Count 8 (+ 2) % Row 4 \SetRowColor{LightBackground} \mymulticolumn{2}{x{8.4cm}}{\seqsplit{`nx.shortest\_path\_length(G},n1,n2)`} \tn % Row Count 9 (+ 1) % Row 5 \SetRowColor{white} `T=nx.bfs\_tree(G, n1)` & Create breadth-first search tree from node n1 \tn % Row Count 12 (+ 3) % Row 6 \SetRowColor{LightBackground} \seqsplit{`nx.average\_shortest\_path\_length(G)`} & Average distance between all pairs of nodes \tn % Row Count 15 (+ 3) % Row 7 \SetRowColor{white} `nx.diameter(G)` & Maximum distance between any pair of nodes \tn % Row Count 17 (+ 2) % Row 8 \SetRowColor{LightBackground} \seqsplit{`nx.eccentricity(G)`} & Returns each node's distance to furthest node \tn % Row Count 20 (+ 3) % Row 9 \SetRowColor{white} `nx.radius(G)` & Minimum eccentricity in the graph \tn % Row Count 22 (+ 2) % Row 10 \SetRowColor{LightBackground} `nx.periphery(G)` & Set of nodes where \seqsplit{eccentricity=diameter} \tn % Row Count 24 (+ 2) % Row 11 \SetRowColor{white} `nx.center(G)` & Set of nodes where eccentricity=radius \tn % Row Count 26 (+ 2) \hhline{>{\arrayrulecolor{DarkBackground}}--} \end{tabularx} \par\addvspace{1.3em} \begin{tabularx}{8.4cm}{x{3.44 cm} x{4.56 cm} } \SetRowColor{DarkBackground} \mymulticolumn{2}{x{8.4cm}}{\bf\textcolor{white}{Connectivity: Network Robustness}} \tn % Row 0 \SetRowColor{LightBackground} \seqsplit{`nx.node\_connectivity(G)`} & Min nodes removed to disconnect a network \tn % Row Count 2 (+ 2) % Row 1 \SetRowColor{white} \seqsplit{`nx.minimum\_node\_cut()`} & Which nodes? \tn % Row Count 4 (+ 2) % Row 2 \SetRowColor{LightBackground} \seqsplit{`nx.edge\_connectivity(G)`} & Min edges removed to disconnect a network \tn % Row Count 6 (+ 2) % Row 3 \SetRowColor{white} \seqsplit{`nx.minimum\_edge\_cut(G)`} & Which edges? \tn % Row Count 8 (+ 2) % Row 4 \SetRowColor{LightBackground} \seqsplit{`nx.all\_simple\_paths(G},n1,n2)` & Show all paths between two nodes \tn % Row Count 10 (+ 2) \hhline{>{\arrayrulecolor{DarkBackground}}--} \end{tabularx} \par\addvspace{1.3em} \begin{tabularx}{8.4cm}{x{3.76 cm} x{4.24 cm} } \SetRowColor{DarkBackground} \mymulticolumn{2}{x{8.4cm}}{\bf\textcolor{white}{Network Connectivity: Connected Components}} \tn % Row 0 \SetRowColor{LightBackground} \seqsplit{`nx.is\_connected(G)`} & Is there a path between every pair of nodes? \tn % Row Count 3 (+ 3) % Row 1 \SetRowColor{white} \seqsplit{`nx.number\_connected\_components(G)`} & \# separate components \tn % Row Count 5 (+ 2) % Row 2 \SetRowColor{LightBackground} \seqsplit{`nx.node\_connected\_component(G}, N)` & Which connected component does {\emph{N}} belong to? \tn % Row Count 8 (+ 3) % Row 3 \SetRowColor{white} \seqsplit{`nx.is\_strongly\_connected(G)`} & Is the network connected directionally? \tn % Row Count 10 (+ 2) % Row 4 \SetRowColor{LightBackground} \seqsplit{`nx.is\_weakly\_connected(G)`} & Is the directed network connected if assumed undirected? \tn % Row Count 13 (+ 3) \hhline{>{\arrayrulecolor{DarkBackground}}--} \end{tabularx} \par\addvspace{1.3em} \begin{tabularx}{8.4cm}{x{3.68 cm} x{4.32 cm} } \SetRowColor{DarkBackground} \mymulticolumn{2}{x{8.4cm}}{\bf\textcolor{white}{Common Graphs}} \tn % Row 0 \SetRowColor{LightBackground} \seqsplit{`G=nx.karate\_club\_graph()`} & Karate club graph (social network) \tn % Row Count 2 (+ 2) % Row 1 \SetRowColor{white} \seqsplit{`G=nx.path\_graph(n)`} & Path graph with n nodes \tn % Row Count 4 (+ 2) % Row 2 \SetRowColor{LightBackground} \seqsplit{`G=nx.complete\_graph(n)`} & Complete graph on n nodes \tn % Row Count 6 (+ 2) % Row 3 \SetRowColor{white} \seqsplit{`G=random\_regular\_graph(d},n)` & Random d-regular graph on n-nodes \tn % Row Count 8 (+ 2) \hhline{>{\arrayrulecolor{DarkBackground}}--} \SetRowColor{LightBackground} \mymulticolumn{2}{x{8.4cm}}{See \{\{popup="http://https://networkx.github.io/documentation/networkx-1.10/reference/generators.html"\}\}NetworkX Graph Generators reference\{\{/link\}\} for more. \newline Also see "An Atlas of Graphs" by Read and Wilson (1998).} \tn \hhline{>{\arrayrulecolor{DarkBackground}}--} \end{tabularx} \par\addvspace{1.3em} \begin{tabularx}{8.4cm}{x{4 cm} x{4 cm} } \SetRowColor{DarkBackground} \mymulticolumn{2}{x{8.4cm}}{\bf\textcolor{white}{Influence Measures and Network Centralization}} \tn % Row 0 \SetRowColor{LightBackground} \seqsplit{`dc=nx.degree\_centrality(G)`} & Degree centrality for network \tn % Row Count 2 (+ 2) % Row 1 \SetRowColor{white} `dc{[}node{]}` & Degree centrality for a node \tn % Row Count 4 (+ 2) % Row 2 \SetRowColor{LightBackground} \seqsplit{`nx.in\_degree\_centrality(G)`}, \seqsplit{`nx.out\_degree\_centrality(G)`} & DC for directed networks \tn % Row Count 7 (+ 3) % Row 3 \SetRowColor{white} \seqsplit{`cc=nx.closeness\_centrality(G},normalized=True)` & Closeness centrality (normalised) for the network \tn % Row Count 10 (+ 3) % Row 4 \SetRowColor{LightBackground} `cc{[}node{]}` & Closeness centrality for an individual node \tn % Row Count 13 (+ 3) % Row 5 \SetRowColor{white} \seqsplit{`bC=nx.betweenness\_centrality(G)`} & Betweenness centrality \tn % Row Count 15 (+ 2) % Row 6 \SetRowColor{LightBackground} `..., normalized=True,...)` & Normalized betweenness centrality \tn % Row Count 17 (+ 2) % Row 7 \SetRowColor{white} `..., endpoints=False, ...)` & BC excluding endpoints \tn % Row Count 19 (+ 2) % Row 8 \SetRowColor{LightBackground} `..., K=10,...)` & BC approximated using random sample of K nodes \tn % Row Count 22 (+ 3) % Row 9 \SetRowColor{white} \seqsplit{`nx.betweenness\_centrality\_subset(G},\{subset\})` & BC calculated on subset \tn % Row Count 25 (+ 3) % Row 10 \SetRowColor{LightBackground} \seqsplit{`nx.edge\_betweenness\_centrality(G)`} & BC on edges \tn % Row Count 27 (+ 2) % Row 11 \SetRowColor{white} \seqsplit{`nx.edge\_betweenness\_centrality\_subset(G},\{subset\})` & BC on subset of edges \tn % Row Count 30 (+ 3) \hhline{>{\arrayrulecolor{DarkBackground}}--} \SetRowColor{LightBackground} \mymulticolumn{2}{x{8.4cm}}{Normalization: Divide by number of pairs of nodes.} \tn \hhline{>{\arrayrulecolor{DarkBackground}}--} \end{tabularx} \par\addvspace{1.3em} \begin{tabularx}{8.4cm}{x{4 cm} x{4 cm} } \SetRowColor{DarkBackground} \mymulticolumn{2}{x{8.4cm}}{\bf\textcolor{white}{PageRank and Hubs \& Authorities Algorithms}} \tn % Row 0 \SetRowColor{LightBackground} `nx.pagerank(G, alpha=0.8)` & Scaled PageRank of G with dampening parameter \tn % Row Count 3 (+ 3) % Row 1 \SetRowColor{white} `h,a=nx.hits(G)` & HITS algorithm - outputs 2 dictionaries (hubs, authorities) \tn % Row Count 6 (+ 3) % Row 2 \SetRowColor{LightBackground} `h,a=nx.hits(G,max\_iter=10,normalized=True)` & Constrained HITS and normalized by sum at each stage \tn % Row Count 9 (+ 3) \hhline{>{\arrayrulecolor{DarkBackground}}--} \SetRowColor{LightBackground} \mymulticolumn{2}{x{8.4cm}}{Centrality measures make different assumptions about what it means to be a "central" node. Thus, they produce different rankings.} \tn \hhline{>{\arrayrulecolor{DarkBackground}}--} \end{tabularx} \par\addvspace{1.3em} \begin{tabularx}{8.4cm}{x{4 cm} x{4 cm} } \SetRowColor{DarkBackground} \mymulticolumn{2}{x{8.4cm}}{\bf\textcolor{white}{Network Evolution - Real-world Applications}} \tn % Row 0 \SetRowColor{LightBackground} `G.degree()`, `G.in\_degree()`, `G.out\_degree()` & Distribution of node degrees \tn % Row Count 3 (+ 3) % Row 1 \SetRowColor{white} Preferential Attachment Model & Results in power law -\textgreater{} many nodes with low degrees; few with high degrees \tn % Row Count 7 (+ 4) % Row 2 \SetRowColor{LightBackground} \seqsplit{`G=barabasi\_albert\_graph(n},m)` & Preferential Attachment Model with {\emph{n}} nodes and each new node attaching to {\emph{m}} existing nodes \tn % Row Count 12 (+ 5) % Row 3 \SetRowColor{white} Small World model & High average degree (global clustering) and low average shortest path \tn % Row Count 16 (+ 4) % Row 4 \SetRowColor{LightBackground} \seqsplit{`G=watts\_strogatz\_graph(n},k,p)` & Small World network of {\emph{n}} nodes, connected to its {\emph{k}} nearest neighbours, with chance {\emph{p}} of rewiring \tn % Row Count 22 (+ 6) % Row 5 \SetRowColor{white} \seqsplit{`G=connected\_watts\_strogatz\_graph(n},k,p, t)` & {\emph{t}} = max iterations to try to ensure connected graph \tn % Row Count 25 (+ 3) % Row 6 \SetRowColor{LightBackground} \seqsplit{`G=newman\_watts\_strogatz\_graph(n},k,p)` & {\emph{p}} = probability of adding (not rewiring) \tn % Row Count 28 (+ 3) % Row 7 \SetRowColor{white} Link Prediction measures & How likely are 2 nodes to connect, given an existing network \tn % Row Count 31 (+ 3) \end{tabularx} \par\addvspace{1.3em} \vfill \columnbreak \begin{tabularx}{8.4cm}{x{4 cm} x{4 cm} } \SetRowColor{DarkBackground} \mymulticolumn{2}{x{8.4cm}}{\bf\textcolor{white}{Network Evolution - Real-world Applications (cont)}} \tn % Row 8 \SetRowColor{LightBackground} \seqsplit{`nx.common\_neighbors(G},n1,n2)` & Calc common neighbors of nodes {\emph{n1}}, {\emph{n2}} \tn % Row Count 3 (+ 3) % Row 9 \SetRowColor{white} \seqsplit{`nx.jaccard\_coefficient(G)`} & Normalised common neighbors measure \tn % Row Count 5 (+ 2) % Row 10 \SetRowColor{LightBackground} \seqsplit{`nx.resource\_allocation\_index(G)`} & Calc RAI of all nodes not already connected by an edge \tn % Row Count 8 (+ 3) % Row 11 \SetRowColor{white} \seqsplit{`nx.adamic\_adar\_index(G)`} & As per RAI but with log of degree of common neighbor \tn % Row Count 11 (+ 3) % Row 12 \SetRowColor{LightBackground} \seqsplit{`nx.preferential\_attachment(G)`} & Product of two nodes' degrees \tn % Row Count 13 (+ 2) % Row 13 \SetRowColor{white} Community Common Neighbors & Common neighbors but with bonus if they belong in same 'community' \tn % Row Count 17 (+ 4) % Row 14 \SetRowColor{LightBackground} \seqsplit{`nx.cn\_soundarajan\_hopcroft(n1}, n2)` & CCN score for {\emph{n1}}, {\emph{n2}} \tn % Row Count 19 (+ 2) % Row 15 \SetRowColor{white} `G.node{[}'A'{]}{[}'community'{]}=1` & Add community attribute to node \tn % Row Count 21 (+ 2) % Row 16 \SetRowColor{LightBackground} \seqsplit{`nx.ra\_index\_soundarajan\_hopcroft(G)`} & Community Resource Allocation score \tn % Row Count 23 (+ 2) \hhline{>{\arrayrulecolor{DarkBackground}}--} \SetRowColor{LightBackground} \mymulticolumn{2}{x{8.4cm}}{These scores give only an indication of whether 2 nodes are likely to connect. \newline To make a link prediction, you would use these scores as features in a classification ML model.} \tn \hhline{>{\arrayrulecolor{DarkBackground}}--} \end{tabularx} \par\addvspace{1.3em} % That's all folks \end{multicols*} \end{document}