{"id":337,"date":"2010-10-20T03:18:57","date_gmt":"2010-10-19T17:18:57","guid":{"rendered":"http:\/\/www.mappingonlinepublics.net\/dev\/2010\/10\/20\/dynamic-networks-in-gephi-from-twapperkeeper-to-gexf\/"},"modified":"2012-04-10T13:47:27","modified_gmt":"2012-04-10T03:47:27","slug":"dynamic-networks-in-gephi-from-twapperkeeper-to-gexf","status":"publish","type":"post","link":"https:\/\/mappingonlinepublics.net\/dev\/2010\/10\/20\/dynamic-networks-in-gephi-from-twapperkeeper-to-gexf\/","title":{"rendered":"Dynamic Networks in Gephi: From Twapperkeeper to GEXF"},"content":{"rendered":"<p>In between last week&#8217;s <a href=\"http:\/\/snurb.info\/taxonomy\/term\/123\">ECREA conference in Hamburg<\/a>, where we presented some of our <a href=\"http:\/\/snurb.info\/node\/1381\">methodologies<\/a> and <a href=\"http:\/\/snurb.info\/node\/1423\">early outcomes<\/a> from the Mapping Online Publics project, and the <a href=\"http:\/\/ir11.aoir.org\/\">AoIR conference in Gothenburg<\/a>, where we&#8217;ll talk some more about tracking and mapping interaction in online social networks, I wanted to finally follow up on <a href=\"http:\/\/www.mappingonlinepublics.net\/dev\/2010\/10\/06\/fun-with-gephis-new-dynamic-visualization-feature\/\">Jean&#8217;s teaser post of a dynamic animation of Twitter @reply activity<\/a> from a couple of weeks ago. This animation of network activity over time has become possible with the release of the latest beta version of <a href=\"http:\/\/gephi.org\/\">Gephi<\/a>, the open source network visualisation software, which now includes support for time-based data &#8211; and on the flight over to Europe as well as in between conferences and workshops, I&#8217;ve made some first steps towards building the tools to prepare our <em>Twitter<\/em> data for such dynamic visualisations.<\/p>\n<p>First, though, I need to stress that the video which we posted a little while ago was only a very preliminary attempt; in the meantime, and with considerable and speedy support from the Gephi team (thanks, guys!), we&#8217;ve managed to improve our methods significantly. In the following, I&#8217;ll explain what our current approach looks like; a little further down the track, we&#8217;ll also post another animation of the results.<\/p>\n<p> <!--more-->  <\/p>\n<p>So, to begin with, we&#8217;re once again starting with a dataset from <em><a href=\"http:\/\/twapperkeeper.com\/\">Twapperkeeper<\/a><\/em>, which tracks user activities around <em>Twitter<\/em> #hashtags. Within these data, the network which we can identify and visualise consists of the @replies between participating users (and there&#8217;s a choice to be made here of whether old-style retweets &#8211; i.e. &#8220;RT @username &#8230;&#8221; should be included or not); <a href=\"http:\/\/www.mappingonlinepublics.net\/dev\/2010\/09\/10\/twitter-reply-networks-on-ausvotes\/\">I&#8217;ve posted previously on our methodology for extracting static @reply networks<\/a>. What we need to do now is extract the @reply information from the <em>Twapperkeeper<\/em> data in such a way as to make it usable for dynamic visualisation in Gephi.<\/p>\n<p>The first step in this is relatively simple, and builds on what we&#8217;ve done before. Using our trusty CSV processing tool <a href=\"http:\/\/www.gnu.org\/software\/gawk\/\">Gawk<\/a>, we&#8217;ll extract all @replies and their (Unix) timestamps &#8211; but in preparation for what we&#8217;ll be doing later, we&#8217;ll already compile all @replies from one specific user to another, and list the timestamps of those @replies. The Gawk script below, then, generates a CSV with lines that look something like this:<\/p>\n<blockquote>\n<p><em>user1<\/em>, <em>user2<\/em>, <em>timestamp1<\/em>;<em>timestamp2<\/em>;<em>timestamp3<\/em>;etc.<\/p>\n<\/blockquote>\n<p>Note in this that @replies are a form of directed edge: <em>user1<\/em> may tweet at <em>user2<\/em>, but that doesn&#8217;t mean that <em>user2<\/em> necessarily replies. So, the resulting CSV file may or may not contain a corresponding line<\/p>\n<blockquote>\n<p><em>user2<\/em>, <em>user1<\/em>, <em>timestamp4<\/em>;<em>timestamp5<\/em>;etc.<\/p>\n<\/blockquote>\n<p>Here&#8217;s the script, which also takes an optional parameter <em>offset<\/em> that is subtracted from all timestamps, just to reduce those very large numbers in the Unix timestamps to something a little more manageable. If no <em>offset<\/em> parameter is given, the script simply takes the timestamp of the first tweet in the dataset as the value for <em>offset<\/em> (in other words, the first tweet now happens at zero seconds).<\/p>\n<p><strong><font color=\"#ff0000\">UPDATE:<\/font> The first version of this script didn&#8217;t make any attempt to combine edges between users occurring with different capitalisation (for example, username1 -&gt; username2 should be identical to UserName1 -&gt; UserName2, since <em>Twitter<\/em> is case-insensitive). This led to false duplicates in Gephi visualisations (username1 and UserName1 would appear as separate nodes). The revised version below unifies usernames, preferring whichever variant appears first in a sorted list (usually the capitalised version, e.g. UserName1 over username1). <\/strong><\/p>\n<\/p>\n<div class=\"wlWriterEditableSmartContent\" id=\"scid:887EC618-8FBE-49a5-A908-2339AF2EC720:90c54806-39d2-4ca2-80e1-0820d53412ee\" style=\"padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px\">         <code><\/p>\n<pre>\n      \n# preparegexfattimeintervals.awk - Extract @replies for network visualisation\n#\n# this script takes a CSV archive of tweets, and reworks it into network data for visualisation\n# data includes the timestamp of the tweet - multiple tweets between two users are concatenated with a semicolon (starttime1;starttime2;starttime3;...)\n#\n# expected data format:\n# text,to_user_id,from_user,id,from_user_id,iso_language_code,source,profile_image_url,geo_type,geo_coordinates_0,geo_coordinates_1,created_at,time\n#\n# output format:\n# source,target,starttimes\n#\n# script takes an optional offset argument, which is subtracted from the timestamp\n# if offset is not set on the command line, the first tweet's timestamp is used instead\n#\n# Released under Creative Commons (BY, NC, SA) by Axel Bruns - a.bruns@qut.edu.au\n\nBEGIN {\n\tgetline \t\t\t\t\t\t\t\t\t# populate the FILENAME variable\n\n\tprint \"source,target,starttimes\"\n\n\t## first, build list of unique usernames in standard capitalisation style\n\t## (to avoid duplicates in different styles, e.g. @UserName vs. @username)\n\n\tgetline < FILENAME\t\t\t\t\t\t\t\t# skip header row\n\n\ti=1\n\twhile(getline < FILENAME) {\t\t\t\t\t\t\t# start first parse of input file\n\t\tnodes[i] = $3\t\t\t\t\t\t\t\t# add any active tweeters\n\t\ti++\n\n\t\tif(starttime == 0) starttime = $13\n\t\tendtime = $13\n\n\t\ta=0\n\t\tdo {\t\t\t\t\t\t\t\t\t# add all @reply recipients\n\t\t\tmatch(substr($1, a),\/@([A-Za-z0-9_]+)?\/,atArray)\n\t\t\ta=a+atArray[1, \"start\"]+atArray[1, \"length\"]\n\t\t\tif (atArray[1] != 0) {\n\t\t\t\tnodes[i] = atArray[1]\n\t\t\t\ti++\n\t\t\t}\n\t\t} while(atArray[1, \"start\"] != 0)\n\t}\n\tclose(FILENAME)\n\n\tn = asort(nodes, sorted)\t\t\t\t\t\t\t# sort and remove duplicates\n\tj = 1\n\n\tfor (i=2; i<=n; i++) {\n\t\tif(tolower(sorted[i]) == tolower(sorted[j])) { \n\t\t\tdelete sorted[i]\n\t\t} else { \n\t\t\tj=i \n\t\t}\n\t}\n\n\tn = asort(sorted,sortednodes)\t\t\t\t\t\t# sort remaining list\n\n}\n\n\/@([A-Za-z0-9_]+)\/ {\t\t\t\t\t\t\t\t\t# main loop: match any line including @reply\n\tif(!offset) offset = $13\t\t\t\t\t\t\t# if not provided as script argument, set offset to first occurring tweet timecode\n\n\ta=0\n\tdo {\t\t\t\t\t\t\t\t\t\t# repeat for each @reply in the line (i.e. execute twice for '@user1 @user2 tweet content')\n\t\tmatch(substr($1, a),\/@([A-Za-z0-9_]+)?\/,atArray)\n\t\ta=a+atArray[1, \"start\"]+atArray[1, \"length\"]\n\n\t\tif (atArray[1] != 0) {\n\t\t\tfor(i in sortednodes) {\t\t\t\t\t# match source and target users against sorted master list of participating users in standard capitalisation\n\t\t\t\tif(tolower(sortednodes[i]) == tolower($3)) { source = sortednodes[i] }\n\t\t\t\tif(tolower(sortednodes[i]) == tolower(atArray[1])) { target = sortednodes[i] }\n\t\t\t}\n\n\t\t\tif(!edge[source \",\" target]) {\n\t\t\t\tedge[source \",\" target] = $13 - offset\t\t# create new edge[source,target] array entry, or\n\t\t\t} else {\n\t\t\t\tedge[source \",\" target] = edge[source \",\" target] \";\" $13 - offset       # add to existing entry\n\t\t\t}\n\t\t}\n\n\t} while(atArray[1, \"start\"] != 0)\n\n}\n\nEND {\n\tfor(i in edge) {\n\t\tprint i \",\" edge[i]\n\t}\n}\n\n        <\/pre>\n<p><\/code>\n      <\/div>\n<\/p>\n<p>So, this preparegexfattimeintervals.awk script, which should be called with the command line<\/p>\n<blockquote>\n<p>gawk -F , -f preparegexfattimeintervals.awk twapperkeeper.csv offset=<em>offset<\/em> &gt;networkedges.csv<\/p>\n<\/blockquote>\n<p>simply lists all edges, and the (potentially multiple) timestamps at which they're created.<\/p>\n<p>If we wanted simply to create a static network visualisation over the entire period covered by the dataset, we'd be nearly done now - we could simply count the number of timestamps attached to each edge, and treat this as the edge weight: an connection between two users which was made only once (i.e. an edge with only one timestamp) would get a weight of 1, while a repeated connection between two users (i.e. multiple @replies from one user to another, at different times) would get a higher weighting and signify a more intensive, closer association between these two users.<\/p>\n<p>However, that's not quite good enough for a dynamic visualisation. Here, we need to define when an edge (i.e. a connection from one user to another) comes into existence, and when it disappears again. The former is easy: we know exactly the timestamp at which each @reply was made, and the point of the previous script was precisely to create - for each pair of users between whom an @reply has been exchanged - a list of the timestamps at which those connections were made.<\/p>\n<p>Now, however, comes the tricky bit: we need to come up with an expiry time. After all, one user sending an @reply to another doesn't mean that those users remain connected forever - far from it: the target user may not see the @reply at all, may not care, may quickly forget again that they received it. However, repeated @replies from one user to another would make the originating user more and more visible (and perhaps memorable) to the recipient, and so we can assume a stronger connection - even more so, of course, if those @replies from user A to user B are reciprocated by user B.<\/p>\n<p>By analogy, the principle is a little similar to the way old-fashioned cathode ray tube TV screens worked: a single electron hitting the screen doesn't create much more than a brief blip which fades away again quickly - but a steady ray of electrons hitting the same spot creates a more permanent image. In essence, what we've got to work out is how soon, on <em>Twitter<\/em>, the light fades away again; we're looking for the decay time of a tweet.<\/p>\n<p>Now, very clearly, there is no easy answer to this, and all we can come up with are some approximate values. Indeed, we could make a credible argument that the decay time depends on the receiving user: someone who gets a lot of @replies within a short period of time might remember each individual one only for a very limited time, and might consider only those users as close connections who @reply very frequently; someone who rarely ever gets @replies might value them considerably more. <\/p>\n<p>But let's not overpsychologise these questions, either. Let's simply assume, for the moment, that a connection made by a single @reply lasts for an hour (3600 seconds), for example, and see what outcomes that produces; we can try other values (say, half an hour, i.e. 1800 seconds) later.<\/p>\n<p>If we apply one of those 'decay times' to our data, then, we're almost ready to start our dynamic visualisation. We now have a start and an end time for each of our @reply edges between two users in the network. Take the example above:<\/p>\n<blockquote>\n<p><em>user1<\/em>, <em>user2<\/em>, <em>timestamp1<\/em>;<em>timestamp2<\/em>;<em>timestamp3<\/em>;etc.<\/p>\n<\/blockquote>\n<p>This would mean that the directed edge from <em>user1<\/em> to <em>user2<\/em> should be visible in the network from <em>timestamp1<\/em> to <em>timestamp1 + 3600s<\/em>, from <em>timestamp2<\/em> to <em>timestamp2 + 3600s<\/em>, and from <em>timestamp3 + timestamp3 + 3600s<\/em>. In <em>Gephi<\/em>'s internal timeline notation, this is relatively easy to express:<\/p>\n<blockquote>\n<p>&lt;[timestamp1, timestamp1 + 3600]; [timestamp2, timestamp2 + 3600]; [timestamp3, timestamp3 + 3600]&gt;<\/p>\n<\/blockquote>\n<p>However, what happens if there's an overlap between these periods: if <em>timestamp2<\/em> is a time which sits within the time during which the tweet made at <em>timestamp1<\/em> is still 'active' (that is, has not expired yet)? <\/p>\n<p>Imagine, for example, that the first tweet is made at 0s, and therefore expires at 3600s, and the second tweet is made half an hour later, at 1800s, and thus expires at 5400s: this would mean that between 0s and 1800s one @reply tweet is active (the connection from <em>user1<\/em> to <em>user2<\/em> should have an edge weight of 1), between 1800s and 3600s two @reply tweets are active (the connection should have an edge weight of 2), and between 3600s and 5400s only the second tweet remains active (the edge weight drops down to 1 again). In reality, of course, things may be considerably more complex still, with multiple overlapping tweets active at the same time.<\/p>\n<p>Here we're getting to the limit of what can be usefully expressed and imported into Gephi in CSV format. There is a way to express dynamically changing edge weights in Gephi notation - for the example above, it would be something like<\/p>\n<blockquote>\n<p>&lt;[0, 1800, 1.0]; [1800, 3600, 2.0]; [3600, 5400, 1.0]&gt;<\/p>\n<\/blockquote>\n<p>- but there doesn't seem to be a way to import such data into Gephi in CSV format, even if it is correctly formatted; all edge weights are reset to 1.<\/p>\n<p>So, we need to switch to the XML-based GEXF format for network description now. GEXF allows us to define 'time slices' during which the weight of an edge can be set to a distinct value. What we need is a script that takes the CSV output of our previous script (a list of directed edges, with their specific start times), and - for any given 'decay time' - calculates the time slices and edge weights that apply. <\/p>\n<p>To do so requires some pretty funky maths, as - depending on the decay time we're using - any number of tweets could be overlapping in various ways at any one point. I <em>think<\/em> the following Gawk script does the trick - but I'd be very grateful for independent verification and improvement suggestions. <strong>Use at your own risk!<\/strong><\/p>\n<\/p>\n<div class=\"wlWriterEditableSmartContent\" id=\"scid:887EC618-8FBE-49a5-A908-2339AF2EC720:a427d5b5-cee7-43d2-8c0b-f699b05601d8\" style=\"padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px\">\n        <code><\/p>\n<pre>\n      \n# gefxattimeintervals.awk - Process CSV of network edges and start times for network visualisation\n#\n# this script takes a CSV of network edges with attached start times (multiples allowed), and converts it into a GEXF for visualisation\n#\n# expected data format:\n# source,target,starttime[;starttime2[;starttime3[;...]]]\n#\n# output format:\n# GEXF network file. Weights of multiple edges whose timeframes overlap are increased.\n#\n# script takes an optional decaytime argument, which sets the expiry time of each edge - edge starting at time t expires at t + decaytime\n# if decaytime is not set on the command line, decaytime is set to 100\n#\n# Released under Creative Commons (BY, NC, SA) by Axel Bruns - a.bruns@qut.edu.au\n\n\nBEGIN {\n\tgetline\n\tprint \"&lt;gexf xmlns=\\\"http:\/\/www.gexf.net\/1.1draft\\\"\\n       xmlns:xsi=\\\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\\\"\\n       xsi:schemaLocation=\\\"http:\/\/www.gexf.net\/1.1draft\\n                             http:\/\/www.gexf.net\/1.1draft\/gexf.xsd\\\"\\n      version=\\\"1.1\\\"&gt;\\n  &lt;graph mode=\\\"dynamic\\\" defaultedgetype=\\\"directed\\\"&gt;\\n  \t&lt;attributes class=\\\"edge\\\" mode=\\\"dynamic\\\"&gt;\\n\t  &lt;attribute id=\\\"weight\\\" title=\\\"Weight\\\" type=\\\"float\\\"\/&gt;\\n\t&lt;\/attributes&gt;\\n\t&lt;edges&gt;\"\n\n\tif(!decaytime) decaytime = 100\n}\n\n{\n\tmax = split($3,time,\";\")\n\n\tfor(i = 1; i &lt;= max; i++) {\n\t\tedgestart[i] = time[i]\n\t\tedgeend[i] = time[i] + decaytime\n\t\tedgeweight[i] = 1\n\t}\n\n\tif(max &gt; 1) {\n\t\tfor(i = 2; i &lt;= max; i++) {\n\t\t\ttop = max\n\t\t\tfor(j = 1; j &lt;= max; j++) {\n\t\t\t\tif((edgestart[i] != edgeend[i]) && (edgestart[j] &lt;= edgestart[i]) && (edgeend[j] &gt; edgestart[i]) && (j != i)) {\n\t\t\t\t\tedgestart[max+1] = edgestart[i]\n\t\t\t\t\tedgeend[max+1] = edgeend[i] &lt; edgeend[j] ? edgeend[i] : edgeend[j]\n\t\t\t\t\tedgeweight[max+1] = edgeweight[i] + edgeweight[j]\n\n# two possibilities here: a) edge i starts inside the edge j timeframe, but finishes later; b) edge i timeframe is completely within edge j timeframe\n\n\t\t\t\t\tif(edgeend[j] &gt; edgeend[i]) {\n\t\t\t\t\t\tedgeend[i] = edgeend[j]\n\t\t\t\t\t\tedgeweight[i] = edgeweight[j]\n\t\t\t\t\t}\n\t\t\t\t\tedgeend[j] = edgestart[max+1]\n\t\t\t\t\tedgestart[i] = edgeend[max+1]\n\t\t\t\t\tmax++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tinterval = weight = slice = \"\"\n\tstart = edgestart[1]\n\tend = edgestart[1]\n\n\tfor(i = 1; i &lt;= max; i++) {\n\t\tif(edgestart[i] != edgeend[i]) {\n\t\t\tif(edgestart[i] &lt; start) start = edgestart[i]\n\t\t\tif(edgeend[i] &gt; end) end = edgeend[i]\n\n\t\t\tslice = slice \"\t\t\t\t&lt;slice start=\\\"\" edgestart[i] \"\\\" end=\\\"\" edgeend[i] \"\\\" \/&gt;\\n\"\n\t\t\tweight = weight \"\t\t\t\t&lt;attvalue for=\\\"weight\\\" value=\\\"\" edgeweight[i] \"\\\" start=\\\"\" edgestart[i] \"\\\" end=\\\"\" edgeend[i] \"\\\"\/&gt;\\n\"\n\n\t\t}\n\t}\n\n\tprint \"\t\t&lt;edge source=\\\"\" $1 \"\\\" target=\\\"\" $2 \"\\\" start=\\\"\" start+0 \"\\\" end=\\\"\" end \"\\\" weight=\\\"0\\\"&gt;\"\n\tprint \"\t\t\t&lt;attvalues&gt;\"\n\tprint weight\n\tprint \"\t\t\t&lt;\/attvalues&gt;\"\n\tprint \"\t\t\t&lt;slices&gt;\"\n\tprint slice\n\tprint \"\t\t\t&lt;\/slices&gt;\"\n\tprint \"\t\t&lt;\/edge&gt;\"\n}\n\nEND {\n\n\tprint \"    &lt;\/edges&gt;\\n  &lt;\/graph&gt;\\n&lt;\/gexf&gt;\"\n\n}\n\n        <\/pre>\n<p><\/code>\n      <\/div>\n<\/p>\n<p>This script, too, takes an optional argument, <em>decaytime<\/em>, which sets the period during which edges in the network are understood to be active. If the <em>decaytime<\/em> argument is not provided, <em>decaytime<\/em> is arbitrarily set to 100. So, in our example above, where tweets are thought to expire after one hour (3600s), you'd call up the script using the following command line:<\/p>\n<blockquote>\n<p>Gawk -F , -f gexfattimeintervals.awk decaytime=3600 networkedges.csv &gt;replynetwork.gexf<\/p>\n<\/blockquote>\n<p>Incidentally, the script <em>only<\/em> generates GEXF data for the network edges; it does not define the nodes (i.e. the <em>Twitter<\/em> users in the network) in the resulting GEXF file. Happily, Gephi is flexible enough to automatically create any unknown nodes which it finds in the data it imports, which allows us to keep the script manageable.<\/p>\n<p>The key content of the GEXF file will look something like this now:<\/p>\n<\/p>\n<div class=\"wlWriterEditableSmartContent\" id=\"scid:887EC618-8FBE-49a5-A908-2339AF2EC720:8d9bee56-03ce-411a-a272-8cfd926ee430\" style=\"padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px\">\n        <code><\/p>\n<pre>\n      \n\t\t&lt;edge source=\"user1\" target=\"user2\" start=\"0\" end=\"5400\" weight=\"0\"&gt;\n\t\t\t&lt;attvalues&gt;\n\t\t\t\t&lt;attvalue for=\"weight\" value=\"1\" start=\"0\" end=\"1800\"\/&gt;\n\t\t\t\t&lt;attvalue for=\"weight\" value=\"2\" start=\"1800\" end=\"3600\"\/&gt;\n\t\t\t\t&lt;attvalue for=\"weight\" value=\"1\" start=\"3600\" end=\"5400\"\/&gt;\n\t\t\t&lt;\/attvalues&gt;\n\t\t\t&lt;slices&gt;\n\t\t\t\t&lt;slice start=\"0\" end=\"1800\" \/&gt;\n\t\t\t\t&lt;slice start=\"1800\" end=\"3600\" \/&gt;\n\t\t\t\t&lt;slice start=\"3600\" end=\"5400\" \/&gt;\n\t\t\t&lt;\/slices&gt;\n\t\t&lt;\/edge&gt;\n\n        <\/pre>\n<p><\/code>\n      <\/div>\n<\/p>\n<p>In other words, this both defines the time slices during which the edge is visible, and the edge weight it has during those times. This, then, we can now import into Gephi, which on import will alert us that it's created all those nodes missing from the GEXF file (if the 'create missing nodes' import option was ticked). <\/p>\n<p>If - like us - you're dealing with a network consisting of a large number of nodes, the first step before getting to any dynamic visualisation might actually be thinning out the network, though - applying a global filter that hides any nodes with an indegree below a specific number, then selecting all of the nodes which have remained visible and copying them to a new workspace (select &gt; right-click &gt; copy to new workspace; then switch to the new workspace). Note that Gephi can occasionally get a little confused about its data at this stage - so it's a good idea to export (not save) the filtered network dataset as a new GEXF file at this point, close the current project in Gephi, and reload the filtered GEXF from scratch. <\/p>\n<p>From here, all we need to do is switch on the timeline filter (the big 'play' button at the bottom of the Gephi screen), and use the sliders to zoom in on specific timeframes in the overall network dataset. In terms of visualising these data, then, there are two major options here:<\/p>\n<ol>\n<li>To run a network visualisation algorithm over the entire dataset until the network settles down, and then switch it off, before selecting specific timeframes: this maps the overall, longer-term network structure, and then (when different timeframes are selected) shows which sections of the network are most active at any one point. In essence, this gives us a sense of the longer-term interconnections between users, which (depending on the overall period covered by the entire dataset) may be more or less permanent, and indicates which clusters in the network converse most actively at any given time.<br \/>\n    <br \/>&#160;&#160; <\/li>\n<li>To run a network visualisation algorithm and keep it switched on while changing the timeframe selection sliders: this shows how individual nodes (users) join and leave the @reply network. In essence, this shows us only the current conversational network, without taking into account any more permanent structures of interconnection. <\/li>\n<\/ol>\n<p>\n  <br \/>So, there's our methodology for dynamic network visualisations as it stands right now. As we find time to process our existing <em>Twapperkeeper<\/em> data, we'll post up some more animations of these dynamic @reply networks (and perhaps do the same for our necessarily more slowly moving blog interlinkage data as well) - but perhaps this methods post might also enable some of you to do some dynamic network mapping of your own. <\/p>\n<p>As always, any feedback would be much appreciated!<\/p>\n<!-- AddThis Advanced Settings generic via filter on the_content --><!-- AddThis Share Buttons generic via filter on the_content -->","protected":false},"excerpt":{"rendered":"<p>In between last week&#8217;s ECREA conference in Hamburg, where we presented some of our methodologies and early outcomes from the Mapping Online Publics project, and the AoIR conference in Gothenburg, where we&#8217;ll talk some more about tracking and mapping interaction in online social networks, I wanted to finally follow up on Jean&#8217;s teaser post of &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/mappingonlinepublics.net\/dev\/2010\/10\/20\/dynamic-networks-in-gephi-from-twapperkeeper-to-gexf\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Dynamic Networks in Gephi: From Twapperkeeper to GEXF&#8221;<\/span><\/a><\/p>\n<p><!-- AddThis Advanced Settings generic via filter on get_the_excerpt --><!-- AddThis Share Buttons generic via filter on get_the_excerpt --><\/p>\n","protected":false},"author":2,"featured_media":1332,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,176,8],"tags":[43,59,23,42,41,9,298],"class_list":["post-337","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-methods","category-processing","category-twitter","tag-replies","tag-dynamic","tag-gephi","tag-mapping","tag-network","tag-twapperkeeper","tag-twitter","entry"],"_links":{"self":[{"href":"https:\/\/mappingonlinepublics.net\/dev\/wp-json\/wp\/v2\/posts\/337","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mappingonlinepublics.net\/dev\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mappingonlinepublics.net\/dev\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mappingonlinepublics.net\/dev\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mappingonlinepublics.net\/dev\/wp-json\/wp\/v2\/comments?post=337"}],"version-history":[{"count":0,"href":"https:\/\/mappingonlinepublics.net\/dev\/wp-json\/wp\/v2\/posts\/337\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mappingonlinepublics.net\/dev\/wp-json\/wp\/v2\/media\/1332"}],"wp:attachment":[{"href":"https:\/\/mappingonlinepublics.net\/dev\/wp-json\/wp\/v2\/media?parent=337"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mappingonlinepublics.net\/dev\/wp-json\/wp\/v2\/categories?post=337"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mappingonlinepublics.net\/dev\/wp-json\/wp\/v2\/tags?post=337"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}