Search code examples
rgephi

Gephi: Import Mixed (Directed and Undirected Type in same graph) Network -- How?


I have a graph where some edges are directed and some are undirected, but I cannot get Gephi to acknowledge both edge types in the same graph.

I am currently using R to manipulate the graph, and then using the 'rgexf' package to encode the graph into a Gephi-readable .gexf file as follows:

write.gexf(nodes = nodes_df, edges = edges_df, edgesWeight = E(gD)$weight,
  nodesAtt = nodes_att, edgesAtt = edges_att, nodesVizAtt = nodes_att_viz, 
  edgesVizAtt = edges_att_viz,output = "plag_c_d3.gexf")

Here, edgesAtt contains a Type column of strings ('Directed' and 'Undirected'). edgesAtt looks like:

Type        weight sourcedate targetdate repost_date_diff
   Directed    100 1361424992 1361426157        0.0134838
 Undirected    100 1362140722 1362140722        0.0000000
   Directed     54 1365403984 1365465600        0.7131481

However, when I open Gephi and open the gexf file, Gephi does not read this 'Type' column as the edge type. Instead, it just treats the 'Type' column like any other arbitrary edge attribute, and it adds a new column called 'Type' filled with the default edge type I choose when opening the dataset. Selecting 'mixed' when I import the data does not change this. Gephi does successfully read the 'weight' column as edge weights, however.

How do I make Gephi see both edge types?

Edit changing the defaultedgetype attribute to 'mixed' or to 'mutual' also does not work, nor does making each undirected edge into two directional edges that face opposite ways.


Solution

  • Based on Yannis P.'s notes on how .gexf files store 'type' in mixed types, I built a quick Python 3 script that solves the problem. The script is below. It assumes you have a .gexf file, and that you've stored 'type' in an edgesAtt column named 'type' that contains strings 'Directed' and 'Undirected'. The script reads these and writes them in to the correct place in 'edges' so that Gephi will recognize them. When you open the new script-generated .gexf file in Gephi, just set the type to 'mixed' when importing.

    note: it's a quick script that eschews error handling and any testing of what it's operating on...use at your own risk and/or add your own catches...but if you're stuck on this as I was, then this should get you started.

    #Call script like:
    #   python this_script.py foo.gexf
    #Assumes that in foo.gexf, Type ('Directed' or 'Undirected') is stored as a string column in edgesAtt
    #Script outputs a file foo_mixed.gexf that is identical except that mixed Directed and Undirected edge types will be specified in edge elements so that Gephi can read them
    
    
    import sys
    import argparse
    
    def work (args):
        linecount = 0 
        notinnodes = False # nodes att comes first in gexf file
    
        # Read the whole .gexf input file into memory as a list
        with open (args.fname, 'r') as infile:
            gexf = infile.readlines() 
        infile.close()
    
        # Create output gexf file
        outfname = args.fname.split('.')[0]+'_mixed'+'.'+args.fname.split('.')[1]
        with open (outfname, 'w') as outgexf:
    
            for line in gexf:
                # First, ignore the node attributes that come before edge atts in .gexf files
                if '<attributes class=\"edge\"' in line:
                    notinnodes = True
    
                # Get the edge attribute number that contains 'Type' or 'type'
                if notinnodes and 'title=\"type\"' in line.lower():
                    Type_attnumber = int(line.split('id=\"att')[1].split('\"')[0])
                    break
    
            # Edit every line that contains an edge element and add the 'type' to it from the attributes listed below it
            for line in gexf:
                if not '<edge id=' in line:
                    outgexf.writelines(line)
                else:
                    edgeLine = line.split('\"')
                    Type = gexf[linecount + 1 + Type_attnumber].split('value=')[1].split('/')[0]
                    outgexf.writelines( '\"'.join(edgeLine[0:6])+'\" '+'type='+Type +'\"'.join(edgeLine[6:]) )
    
                linecount = linecount+1
    
    def main():
        # Parser to grab name of file we're currently working on
        parser = argparse.ArgumentParser()
        parser.add_argument("fname")
        args = parser.parse_args()
        work(args)
    
    if __name__ == "__main__":
        main()