saving multiple qgraphs as subplots in one figure - qgraphicsview

I made four correlation networks by using qgraph package and now I would like to save these two graphs in one figure with labels a, b, c, d.
I am using ggarrange(a, b, c, d, ncol=2, nrow=2, label="auto") but getting this warning below.
Warning messages:
1: In as_grob.default(plot) :
Cannot convert object of class qgraph into a grob.
I know it is not straight forward to export qgraph objects via ggarrange function but I do not know the right way of doing this. Anyone has any idea how this can be solved and I can save four network graphs as one .jpeg image?
thanks!

Related

Attempting to use choropleth maps in folium for first time, index error

This is my first time posting to stackoverflow, so I hope I am doing this correctly. I am currently finishing up a 'jumpstart' introduction to data analytics. We are utilizing Python with a few different packages, such as pandas, seaborn, folium etc. For part of our final project/presentation, I am trying to make a zipcode choropleth map. I have successfully imported folium and have my map displayed - the choropleth concept is new to me and is completely extracurricular. Trying to challenge myself to succeed.
I found an example of creating a choropleth map here that I am trying to use: https://medium.com/#saidakbarp/interactive-map-visualization-with-folium-in-python-2e95544d8d9b. I believe I correctly substituted the different object names for the data frame and map name that I am working with. For the geoJSON data, I found this https://github.com/OpenDataDE/State-zip-code-GeoJSON. I opened the geoJSON file in Atom, and found the feature title for what I believe to be the five digit zipcode 'ZCTA5CE10'.
Here is my code:
folium.Choropleth(geo_data='../data/tn_tennessee_zip_codes_geo.min.json',
data=slow_to_resolve,
columns=['zipcode'],
key_on='feature.properties.ZCTA5CE10',
fill_color='BuPu', fill_opacity=0.7, line_opacity=0.2,
legend_name='Zipcode').add_to(nash_map)
folium.LayerControl().add_to(nash_map)
nash_map
When I try to run the code, I get this error:
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-114-a2968de30f1b> in <module>
----> 1 folium.Choropleth(geo_data='../data/tn_tennessee_zip_codes_geo.min.json',
2 data=slow_to_resolve,
3 columns=['zipcode'],
4 key_on='feature.properties.ZCTA5CE10',
5 fill_color='BuPu', fill_opacity=0.7, line_opacity=0.2,
~\anaconda3\lib\site-packages\folium\features.py in __init__(self, geo_data, data, columns, key_on, bins, fill_color, nan_fill_color, fill_opacity, nan_fill_opacity, line_color, line_weight, line_opacity, name, legend_name, overlay, control, show, topojson, smooth_factor, highlight, **kwargs)
1198 if hasattr(data, 'set_index'):
1199 # This is a pd.DataFrame
-> 1200 color_data = data.set_index(columns[0])[columns[1]].to_dict()
1201 elif hasattr(data, 'to_dict'):
1202 # This is a pd.Series
IndexError: list index out of range
Prior to this error, I had two columns from my dataframe specified, but I got some 'isnan' error that I am pretty sure was attributed to string type data in the second column, so I removed it. Now currently trying to figure out this posted error.
Can someone point me in the right direction? Please keep in mind that aside from this three week jumpstart program, I have zero programming knowledge or experience - so I am still learning terminology and concepts.
Thank you!
The error you got was an IndexError: list index out of range because you provided to the columns parameter with just one column columns=['zipcode']. It has to be two like this: columns=['zipcode', 'columnName_to_color_map'].
Where the first column 'zipcode' must match the object node in the GeoJSON file data (note the format/type must also match, that is string: '11372' IS NOT integer: 11372). The second column 'columnName_to_color_map' or whatever name you used should be the column which defined the choropleth colors.
Also note that key_on should match the first column 'zipcode'.
So the code should look like this:-
folium.Choropleth(geo_data='../data/tn_tennessee_zip_codes_geo.min.json',
data=slow_to_resolve,
columns=['zipcode', 'columnName_to_color_map'],
key_on='feature.properties.ZCTA5CE10',
fill_color='BuPu', fill_opacity=0.7, line_opacity=0.2,
legend_name='Zipcode').add_to(nash_map)
folium.LayerControl().add_to(nash_map)
nash_map

Creating a surface plot from an Unstructured grid vtk file using Vedo

I have an unstructured grid vtk file that contains three different types of cells (Tetrahedral, Wedge and Hexahedral). This file contains multiple Scalars (8 attributes such as Pressure, Temperature e.t.c.) and a Single Vector (U,V,W) and I am trying to create a surface plot from this file for a Scalar or Vector at a time using the Vedo python wrapper for vtk. The vtk file contains a scalar or vector value for each cell, including the point coordinates.
I have read the documentation over and over, with examples here https://vtkplotter.embl.es/content/vtkplotter/index.html. These are the things that I have tried with the challenge that I am having with each method:
Method 1: Loading the file as a TetMesh
vp = Plotter()
test = load('Case_60.vtk')
vp.show(test)
This method doesn't plot Scalar Values and only shows points. No Solid Surface. Tried using a cuttertool() with it , it throws an error saying non-Tetrahedral Cell Encountered.
Method 2: Using the UGrid
ug = UGrid('Case_60.vtk')
show(ug)
This method plots as surface with a solid color. Does not seem to be picking the Scalars.
What is the proper way for me to display surface plot and display the scalar value for each cell? Is Vedo able to do what I'm trying to do?
You might need to specify which array is to be used for coloring, e.g.:
from vedo import *
ug = UGrid(datadir+'limb_ugrid.vtk')
print(ug.getArrayNames())
ug.selectCellArray('chem_0')
show(ug, axes=True)
if this doesn't work for your mesh please submit an issue here.

Display seaborn plots at some point later in code

Let's say at some point in my code, I have following two graphs: i.e. graph_p_changes and graph_p_contrib
line_grapgh_p_changes = df_p_change[['year','interest accrued', 'trade debts', 'other financial assets']].melt('year', var_name='variables', value_name='p_changes')
graph_p_changes = sns.factorplot(x="year", y="p_changes", hue='variables', data=line_grapgh_p_changes, height=5, aspect=2)
graph_p_changes.set(xlabel='year', ylabel='percentage change in self value across the years')
line_grapgh_p_contrib = df_p_contrib[['year','interest accrued', 'trade debts', 'other financial assets']].melt('year', var_name='variables', value_name='p_changes')
graph_p_contrib = sns.factorplot(x="year", y="p_changes", hue='variables', data=line_grapgh_p_contrib, height=5, aspect=2)
graph_p_contrib.set(xlabel='year', ylabel='percentage chnage in contribution to total value')
Now at some point later in my code, I just want to display one of the above two graphs. But when I do plt.show(), it displays both of the above graphs in my jupyter notebook. How can I display only one graph at any point in my code.
You'll want to refer to the assigned variable for each plot and then add .fig after that to redisplay it in a Jupyter notebook cell.
Specifically, in your case you'd reference graph_p_changes.fig or graph_p_contrib.fig in a cell and execute that cell to see an individual plot again.
This is similar to how you can show Seaborn's ClusterGrids again, see here. Because the title of your question said 'seaborn plots', I'll add for sake of completeness, this doesn't hold for plots like Seaborn's line plot (lineplot) or bar plot (barplot) , that produce AxesSubplot objects. There you use .figure, for example ax.figure to recall most of the examples listed on Seaborn's lineplot documentation.
Example catplots with code
This is using example code from here and seaborn's catplot documentation (see below) to make two plots. If this code was in one cell and then that cell was run, you'd see two plots in the output below it.
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
titanic = sns.load_dataset("titanic")
exercise = sns.load_dataset("exercise")
g = sns.catplot("alive", col="deck",
col_wrap=3, data=titanic[titanic.deck.notnull()],
kind="count", height=2.5, aspect=.8)
another_plot = sns.catplot(x="time", y="pulse", hue="kind", data=exercise)
Later, each can be displayed again individually as output of other cells with g.fig or another_plot.fig, depending on which plot you want to show.
Additionaly, I'll suggest to improve your long-term code viability, you may want to move on to using catplot in your plotting calls as that is what factorplot is now called in seaborn. See here where it says "factorplot still exists and will pass its arguments through to catplot() with a warning. It may be removed eventually, but the transition will be as gradual as possible."
UPDATE:
OP commented that what was desired was code allowing interspersed stdout/stderr output with plots at precise points among that stream and not just at the end.
For some reason, Seaborn plots (even simple line plots) don't seem to get 'captured' correctly with io.capture_output(), and so I had to use the %%capture cell magic command in the producing cell and combine the output in a separate cell. However, Plotly plots I tried based on example code are captured by io.capture_output() and allow facile intermixing all in the same cell. This is all illustrated in an example notebook available here; it is best viewed in static form here at nbviewer because nbviewer renders the Plotly plots while GitHub doesn't. The top of that notebook includes a link where you can launch an active Jupyter session where it will run.
Update related to this UPDATE:
In an insightful answer to 'seaborn stop figure from being visualized', ffrosch suggests you "can temporarily disable/enable the inline creation with plt.ioff() and plt.ion()." This may offer yet another way to fine-tune when Seabor plots show among the output and/or offer another way to constrain ouput since %%capture cell magic worked yet io.capture_output() did not. (I have yet to try this.)

Is it possible to modify the contents of a text object using Python?

I have a dictionary file called “labels” that contains text objects.
Screen capture of file
When I display the contents of this file, I get the following:
{'175.123.98.240': Text(-0.15349206308126684, -0.6696533109609498, '175.123.98.240'),
'54.66.152.105': Text(-1.0, -0.5455880938500245, '54.66.152.105'),
'62.97.116.82': Text(0.948676253595717, 0.6530664635187481, '62.97.116.82'),
'24.73.75.234': Text(0.849485905682265, -0.778703553136851, '24.73.75.234'),
'1.192.128.23': Text(0.2883091762715677, -0.03432011446968225, '1.192.128.23'),
'183.82.9.19': Text(-0.8855214994079628, 0.7201660238351776, '183.82.9.19'),
'14.63.160.219': Text(-0.047457773060320695, 0.655032585063581, '14.63.160.219')}
I want to change the IP address in the text object portion such that the file looks like this:
{'175.123.98.240': Text(-0.15349206308126684, -0.6696533109609498, 'xxx.123.98.240'),
'54.66.152.105': Text(-1.0, -0.5455880938500245, 'xxx.66.152.105'),
'62.97.116.82': Text(0.948676253595717, 0.6530664635187481, 'xxx.97.116.82'),
'24.73.75.234': Text(0.849485905682265, -0.778703553136851, 'xxx.73.75.234'),
'1.192.128.23': Text(0.2883091762715677, -0.03432011446968225, 'xxx.192.128.23'),
'183.82.9.19': Text(-0.8855214994079628, 0.7201660238351776, 'xxx.82.9.19'),
'14.63.160.219': Text(-0.047457773060320695, 0.655032585063581, 'xxx.63.160.219')}
This file is used for printing labels on a networkx graph.
I have a couple of questions.
Can the contents of a text object be modified?
If so, can it be changed without iterating through the file since the number of changes could range from 3 to 6,000, depending on what I am graphing?
How would I do it?
I did consider changing the IP address before I created my node and edge files but that resulted in separate IP address being clustered incorrectly. For example: 173.6.48.24 and 1.6.48.24 would both be converted to xxx.6.48.24.
Changing the IP address at the time of printing the labels seems like the only sensible method.
I am hoping someone could point me in the right direction. I have never dealt with text objects and I am out of my depth on this one.
Thanks
Additional information
The original data set is a list of IP addresses that have attack several honeypots I am running. I have taken the data and catalogued the data based on certain attack criteria.
The data that I showed was just one of the small attack networks. The label file was generated using the code:
labels = nx.draw_networkx_labels(compX, pos=pos_df)
Where compX is the file containing the data to be graphed and pos_df is the layout of the graph. In this case, I used nx.spring_layout().
I can display the contents of the label file using:
for k,v in labels.items():
print(v)
However, “v” contains the text object, which I do not seem to be able to work with. The content of “v” is a follows:
Text(-0.15349206308126684, -0.6696533109609498, '175.123.98.240')
Text(-1.0, -0.5455880938500245, '54.66.152.105')
Text(0.948676253595717, 0.6530664635187481, '62.97.116.82')
Text(0.849485905682265, -0.778703553136851, '24.73.75.234')
Text(0.2883091762715677, -0.03432011446968225, '1.192.128.23')
Text(-0.8855214994079628, 0.7201660238351776, '183.82.9.19')
Text(-0.047457773060320695, 0.655032585063581, '14.63.160.219')
This is where I am stuck. I do not seem to be able to come up with any code that does not return some kind of “'Text' object has no attribute xxxx”.
As for replacing the first octet, I have the following code that works on a dataframe and I have just been experimenting to see if I can adapt it but so far, no luck:
df[column_ID] = df[column_ID].apply(lambda x: "xxx."+".".join(x.split('.')[1:4])) # Replace First octet
As I said, I would prefer not to iterate through the file. This cluster has seven entries; others can contain up to 6,000 nodes – granted the graph looks like a hairball with this many nodes, but most are between 3 and 25 nodes. I have a total of 60 clusters and as I collect more information, this number will rise.
I found a solution to replacing text inside a text object:
1) Convert text object to string
2) Find the position to be changed and make the change
3) Use set_text() to make the change to the text object
Example code
# Anonymize Source IP address
for k,v in labels.items():
a = str(v)
a = a[a.find(", '"):]
a = 'xxx' + a[a.find("."):][:-2]
v.set_text(a)

Basic importing coordinates into R and setting projection

Ok, I am trying to upload a .csv file, get it into a spatial points data frame and set the projection system to WGS 84. I then want to determine the distance between each point This is what I have come up with but I
cluster<-read.csv(file = "cluster.csv", stringsAsFactors=FALSE)
coordinates(cluster)<- ~Latitude+Longitude
cluster<-CRS("+proj=longlat +datum=WGS84")
d<-dist2Line(cluster)
This returns an error that says
Error in .pointsToMatrix(p) :
points should be vectors of length 2, matrices with 2 columns, or inheriting from a SpatialPoints* object
But this isn't working and I will be honest that I don't fully comprehend importing and manipulating spatial data in R. Any help would be great. Thanks
I was able to determine the issue I was running into. With WGS 84, the longitude comes before the latitude. This is just backwards from how all the GPS data I download is formatted (e.g. lat-long). Hope this helps anyone else who runs into this issue!
thus the code should have been
cluster<-read.csv(file = "cluster.csv", stringsAsFactors=FALSE)
coordinates(cluster)<- ~Longitude+Latitude
cluster<-CRS("+proj=longlat +datum=WGS84")

Resources