find nodes with multiple parents using networkx - networkx

Let's say I have a directed graph (this is about relational tables). I want to find M:N tables to track relationships enabled thru M:N tables.
from pathlib import Path
import subprocess
import networkx as nx
def write_svg(g, name):
temp = "temp.dot"
suffix = "jpg"
nx.nx_agraph.write_dot(g, temp)
pa_img = Path(f"{name}.{suffix}")
li_cmd = f"/opt/local/bin/dot {temp} -T {suffix} -o {pa_img}".split()
subprocess.check_output(li_cmd)
G = nx.DiGraph()
G.add_edge("C1", "P1")
G.add_edge("C2", "P1")
G.add_edge("C21", "C2")
G.add_edge("MN12", "P1")
G.add_edge("MN12", "P2")
G.add_nodes_from([
("MN12", {"color" : "red"})
])
Run this, and I get:
So what I am considering here is that MN12 has as parents P1 and P2. So I want to consider P2 to be related to P1, with MN12 as the mapping table.
In other words, if I hard-code the relationship graph I want:
G = nx.DiGraph()
G.add_edge("C1", "P1")
G.add_edge("C2", "P1")
G.add_edge("C21", "C2")
G.add_edge("P2(using MN12)", "P1")
G.add_nodes_from([
("P2(using MN12)", {"color" : "green"})
])
Note that C21 remains a child of C2. Only MN12 is modified, because it has 2 parents.
Now, I know I can see the degree of a given node.
Going in back to my input graph:
(Pdb++) G.degree('MN12')
2
(Pdb++) G.degree('C1')
1
(Pdb++) G.degree('C2')
2
(Pdb++) G.degree('P1')
3
But how do I see that the arrows from MN12 go towards both P1 and P2? Is this even a question for networkx?

You can use both out_edges and successors.
>>> G.out_edges("MN12")
OutEdgeDataView([('MN12', 'P1'), ('MN12', 'P2')])
>>> list(G.successors("MN12"))
['P1', 'P2']

Related

Django ORM - Select All Records from One Table That Do Not Exist in Another Table

Lets have 2 models:
class A(models.Model):
f1 = models.CharField()
f2 = models.IntegerField()
f3 = models.BooleanField()
class B(models.Model):
f1 = models.CharField()
f2 = models.IntegerField()
f3 = models.DecimalField()
Lets have this data:
A(f1=rat, f2=100, f3=true)
A(f1=cat, f2=200, f3=true)
A(f1=dog, f2=300, f3=false)
B(f1=eagle, f2=100, f3=3.14)
B(f1=cat, f2=200, f3=9.81)
B(f1=dog, f2=300, f3=100.500)
I need to select objects from table B, that does not have similar data for fields f1, f2 in table A.
In my case it will be:
B(f1=eagle, f2=100, f3=3.14)
The following objects are not relevant, because they exist in both tables (f1 and f2 fields)
B(f1=cat, f2=200, f3=9.81)
B(f1=dog, f2=300, f3=100.500)
Is it possible to select this data using Django ORM?
I tried to find information about Sub-query, but did not find good example.
You can use exclude with Q objects:
from django.db.models import Q
B.objects.exclude(Q(f1__in=A.objects.values_list('f1', flat=True)) & Q(f2__in=A.objects.values_list('f2', flat=True)))
resolved in this way:
from django.db.models import OuterRef, Exists
a_queryset = A.objects.filter(f1=OuterRef('f1'), f2=OuterRef('f2'))
result_queryset = B.objects.filter(~Exists(a_queryset))
# B(f1=eagle, f2=100, f3=3.14)
reverse:
result_queryset = B.objects.filter(Exists(a_queryset))
# B(f1=cat, f2=200, f3=9.81)
# B(f1=dog, f2=300, f3=100.500)

Combine Graphs in Networkx: Adding Graphs as Daughter Nodes

I have two Graphs.
Graph_1 is a Directed Acyclic Graph (DAG) which has the following edge list in df_1:
node_1 node_2
John Charity
John Constantine
Gordon John
Gordon Nick
Graph_1 = nx.from_pandas_edgelist(df_1, source="node_1",
target="node_2", create_using=nx.DiGraph())
Graph_2 is a random stochastic graph which is generated as follows:
Graph_2 = nx.erdos_renyi_graph(1000, 0.1)
I would like to join Graph_2 to Graph_1 by making the node with the highest betweenness centrality in Graph_2 a child node of the "Nick" node in Graph_1.
Does anyone have any ideas on how I could do this?
Following should work
import networkx as nx
import matplotlib.pylab as pl
edge_list = [
["John", "Charity"],
["John", "Constantine"],
["Gordon", "John"],
["Gordon", "Nick"], ]
Graph_1 = nx.from_edgelist(edge_list, create_using=nx.DiGraph())
# reduced the number for visualization
Graph_2 = nx.erdos_renyi_graph(10, 0.1)
node_with_highest_betweenness_centrality = max(nx.betweenness_centrality(Graph_2).items(), key=lambda x: x[1])[0]
joined_graph = nx.DiGraph(Graph_1)
joined_graph.add_edges_from(Graph_2.edges())
# not sure which direction you want
joined_graph.add_edge(node_with_highest_betweenness_centrality, "Nick")
nx.draw(joined_graph, with_labels=True)
pl.show()

Open Street Map using OSMNX: how to retrieve the Hannover subway network?

import osmnx as ox
ox.__version__ # '0.13.0'
I would like to show the subway in Hannover as known in the German subway OSM data on a map using the great OSMNX module. But unlike the New York example no results are returned for:
G = ox.graph_from_place('Hannover, Germany',
retain_all=False, truncate_by_edge=True, simplify=True,
network_type='none', custom_filter='["railway"~"subway"]')
# EmptyOverpassResponse: There are no data elements in the response JSON
I do get results for other similar queries using 'Hannover, Germany' as region. I also do not get subway results for Paris or London. And I do not get results for similar queries like custom_filter='["railway"~"tram"]' or '["railway"~"s-bahn"]' or '["network"~"metro"]'.
Also, if I use the infrastructure keyword argument to select "railway", an extensive gdf is returned:
G = ox.graph_from_place('Hannover, Germany', retain_all=False, truncate_by_edge=True, simplify=True,
network_type='none', infrastructure='way["railway"]')
gdfox = ox.graph_to_gdfs(G, nodes=False, edges=True, node_geometry=True, fill_edge_geometry=True)
gdfox.shape # (4422, 14)
But I cannot identify the subway using the columns returned?:
['u', 'v', 'key', 'osmid', 'service', 'oneway', 'length',
'geometry', 'name', 'maxspeed', 'ref', 'bridge', 'tunnel',
'access']
What I also find strange is that there are only 2 LINESTRINGS returned if I (try to) retrieve all railways using the custom_filter:
G = ox.graph_from_place('Hannover, Germany', retain_all=False, truncate_by_edge=True,
simplify=True, network_type=None, custom_filter='["railway"~""]')
gdfox = ox.graph_to_gdfs(G, nodes=False, edges=True, node_geometry=True, fill_edge_geometry=True)
gdfox.shape # (2, 10) # returns only 2 LINESTRINGS: Altenbekener Damm
I am in the process of removing the infrastructure parameter in favor of a more consistent custom_filter parameter. Will be done in a couple days: https://github.com/gboeing/osmnx/pull/477 (EDIT: done and released in v0.14.0; code snippet below edited accordingly.)
In the meantime, I am not familiar with Hannover but it appears that its passenger rail system is tagged as "tram" and "rail" rather than "subway". Something like this seems to capture it:
import osmnx as ox
ox.config(use_cache=False,
log_console=True,
useful_tags_way=ox.settings.useful_tags_way + ['railway'])
G = ox.graph_from_place('Hannover, Germany',
retain_all=False, truncate_by_edge=True, simplify=True,
custom_filter='["railway"~"tram|rail"]')
len(G) #1776

Aligning and italicising table column headings using Rmarkdown and pander

I am writing a rmarkdown document knitting to pdf with tables taken from portions of lists from the ezANOVA package. The tables are made using the pander package. Toy Rmarkdown file with toy dataset below.
---
title: "Table Doc"
output: pdf_document
---
```{r global_options, include=FALSE}
#set global knit options parameters.
knitr::opts_chunk$set(fig.width=12, fig.height=8, fig.path='Figs/',
echo=FALSE, warning=FALSE, message=FALSE, dev = 'pdf')
```
```{r, echo=FALSE}
# toy data
id <- rep(c(1,2,3,4), 5)
group1 <- factor(rep(c("A", "B"), 10))
group2 <- factor(rep(c("A", "B"), each = 10))
dv <- runif(20, min = 0, max = 10)
df <- data.frame(id, group1, group2, dv)
```
``` {r anova, echo = FALSE}
library(ez)
library(plyr)
library(pander)
# create anova object
anOb <- ezANOVA(df,
dv = dv,
wid = id,
between = c(group1, group2),
type = 3,
detailed = TRUE)
# extract the output table from the anova object, reduce it down to only desired columns
anOb <- data.frame(anOb[[1]][, c("Effect", "F", "p", "p<.05")])
# format entries in columns
anOb[,2] <- format( round (anOb[,2], digits = 1), nsmall = 1)
anOb[,3] <- format( round (anOb[,3], digits = 4), nsmall = 1)
pander(anOb, justify = c("left", "center", "center", "right"))
```
Now I have a few problems
a) For the last three columns I would like to have the column heading in the table aligned in the center, but the actual column entries underneath those headings aligned to the right.
b) I would like to have the column headings 'F' and 'p' in italics and the 'p' in the 'p<.05' column in italics also but the rest in normal font. So they read F, p and p<.05
I tried renaming the column headings using plyr::rename like so
anOb <- rename(anOb, c("F" = "italic(F)", "p" = "italic(p)", "p<.05" = ""))
But it didn't work
In markdown, you have to use the markdown syntax for italics, which is wrapping text between a star or underscore:
> names(anOb) <- c('Effect', '*F*', '*p*', '*p<.05*')
> pander(anOb)
-----------------------------------------
Effect *F* *p* *p<.05*
--------------- ------ -------- ---------
(Intercept) 52.3 0.0019 *
group1 1.3 0.3180
group2 2.0 0.2261
group1:group2 3.7 0.1273
-----------------------------------------
If you want to do that in a programmatic way, you can also use the pandoc.emphasis helper function to add the starts to a string.
But your other problem is due to a bug in the package, for which I've just proposed a fix on GH. Please feel free to give a try to that branch and report back on GH -- I will try to get some time later this week to clean up the related unit tests and merge the branch if everything seem to be OK.

Read .txt file value after a certain string (Matlab)

I'm trying to obtain the displacements of a node from an FEA .txt results file using matlab. I want to search for the node (e.g. 5151) and then read the displacements, the problem is the node is mentioned a couple of times before getting to the deformations.
The part of the results I'm interested in looks like this......
N O D E O U T P U T
THE FOLLOWING TABLE IS PRINTED FOR NODES BELONGING TO NODE SET ASSEMBLY_TIP_NODES
NODE FOOT- U1 U2 U3 UR1 UR2 UR3
NOTE
5101 2.6105E-03 -1.1943E-02 6.0023E-03 -8.6770E-02 -1.6432E-02 -1.1048E-02
5102 2.5224E-03 -1.1267E-02 5.6868E-03 -8.6763E-02 -1.6390E-02 -1.0943E-02
5103 2.4340E-03 -1.0589E-02 5.3709E-03 -8.6725E-02 -1.6269E-02 -1.0666E-02
5104 2.3455E-03 -9.9121E-03 5.0549E-03 -8.6542E-02 -1.6052E-02 -1.0267E-02
5105 2.2575E-03 -9.2374E-03 4.7396E-03 -8.6041E-02 -1.5708E-02 -9.7843E-03
5106 2.1710E-03 -8.5682E-03 4.4262E-03 -8.5111E-02 -1.5217E-02 -9.2317E-03
5107 2.0869E-03 -7.9085E-03 4.1164E-03 -8.3688E-02 -1.4585E-02 -8.6334E-03
5108 2.0063E-03 -7.2623E-03 3.8120E-03 -8.1750E-02 -1.3825E-02 -8.0049E-03
5109 1.9299E-03 -6.6336E-03 3.5150E-03 -7.9308E-02 -1.2958E-02 -7.3631E-03
5110 1.8586E-03 -6.0265E-03 3.2271E-03 -7.6400E-02 -1.2011E-02 -6.7206E-03
5111 1.7928E-03 -5.4442E-03 2.9500E-03 -7.3084E-02 -1.1010E-02 -6.0892E-03
5112 1.7329E-03 -4.8897E-03 2.6851E-03 -6.9435E-02 -9.9818E-03 -5.4777E-03
5113 1.6791E-03 -4.3652E-03 2.4334E-03 -6.5533E-02 -8.9528E-03 -4.8933E-03
etc, so what I want to do is search for a unique string ' N O D E O U T P U T' and then search for '5151' and import its displacements into an array.
Example output
nodeDisp = 1.6791E-03 -4.3652E-03 2.4334E-03 -6.5533E-02 -8.9528E-03 -4.8933E-03