Graph of objects using graph-tool - python

I'm trying to use the library graph-tool to create a new graph of objects. In particular I want some nodes of students and others of professors.
In the following the code I've written, I'm not able to add the vertices of students/professors.
The documentations https://graph-tool.skewed.de/static/doc/quickstart.html#property-maps explains that I have to use the class PropertyMap, it's quite weird for me...
Could someone explain to me how to use it? Thanks.
This is my code:
from graph_tool.all import *
class Student:
def __init__(self, name):
self.name = name
class Professor:
def __init__(self, name):
self.name = name
g = Graph()
s = Student("Luke")
p = Professor("Adam")
print(s.name)
print(p.name)

Well first you need to add the vertices to the graph
# your code
# from graph_tool.all import * # avoid importing all, it makes it inclear what you are using afterwards
import graph_tool as gt
class Student:
def __init__(self, name):
self.name = name
class Professor:
def __init__(self, name):
self.name = name
g = gt.Graph()
s = Student("Luke")
p = Professor("Adam")
print(s.name)
print(p.name)
Luke
Adam
you create a property map with for the type of your users
g.vertex_properties['user_type'] = g.new_vertex_property('string')
This will be a property for your nodes. You can create how many you want and then set their user_type
For exemple :
# creating a graph of 3 users
g.add_vertex(3)
# Let's set the first vertex as a professor
g.vp['user_type'][g.vertex(0)] = 'professor'
# and the subsequent as students
for v in g.get_vertices()[1:]:
g.vp['user_type'][v] = 'student'
# let's add an edge from the professor to the first student
g.add_edge(g.vertex(0), g.vertex(1))
<Edge object with source '0' and target '1' at 0x7f2c30370c30>
Now you can draw your graph using the property
def translate_elts_to_gtshape(g, source_prop='user_type', tdict=None):
"""This function adds a new property map called 'shape' to graph g. It is populated by translating the source_prop with the tdict (translation dictionary) which should map source_property entries to shapes"""
if tdict is None:
tdict = {'professor': 'square',
'student': 'circle'}
# source_property map array. It's of string type so we use get_2d_array
svp = g.vp[source_prop].get_2d_array([0])[0]
for k, v in tdict.items():
# for each key, value pair of tdict we change the source value (ie k) to the shape (v)
svp[svp == k] = v
# and now we create the new property shape
g.vertex_properties['shape'] = g.new_vertex_property('string')
for v, p in zip(g.vertices(), svp):
# and copy in it the shape
g.vp['shape'][v] = p
# we return the new property created, although it is already added to the graph as an internal property
return g.vp['shape']
translate_elts_to_gtshape(g)
<PropertyMap object with key type 'Vertex' and value type 'string', for Graph 0x7f2c5c3d6510, at 0x7f2c2997c950>
# let's just check what we have in our newly created shape property
[g.vp['shape'][v] for v in g.vertices()]
['square', 'circle', 'circle']
# and now let's use it to draw our graph
import graph_tool.draw as gtd
pos = gtd.sfdp_layout(g)
gtd.graph_draw(g, pos=pos,
output_size = (300, 300),
output_file = 'mynetwork.png', # where your file goes
vertex_shape=g.vp['shape'],
vertex_text=g.vertex_index)
<PropertyMap object with key type 'Vertex' and value type 'vector<double>', for Graph 0x7f2c5c3d6510, at 0x7f2c14291590>

Related

How to avoid linking of objects in a list?

My problem is quite simple but I am unable to solve it. When I insert objects into a list, the elements of the list all change whenever I change one of them (they all point to the same object in the memory I think). I want to unlink them so the list would not be full of the exactly same objects with the same values. E.g. avoid linking or mutability. I think the problem is how I initialize the objects but I am not sure how to solve it. Here is my code.
from typing import List, Tuple
class State:
#think of State as some kind of coordinates
def __init__(self, z:float, angle:float):
self.z = z
self.angle = angle
class ListOfStates:
#this should be an object with a list containing DIFFERENT (unlinked) State objects
def __init__(self, list_of_states : List[State]):
self.list_of_states = list_of_states
class StateSettings:
#a bigger object to encapsulate previous objects
def __init__(self, state : State, list_of_states : ListOfStates):
self.state = state
self.list_of_states = list_of_states
some_number = 42
# my try #1
state_settings = StateSettings
#create a list of State objects to be used later
state_settings.list_of_states = [State for i in range(some_number)]
state_settings.state = State
for i in range(some_number):
state_settings.list_of_states[i].angle = i
And state_settings.list_of_states contains the same copy of the object 42 times, e.g.
print(state_settings.list_of_states[0].angle)
print(state_settings.list_of_states[1].angle)
print(state_settings.list_of_states[2].angle)
prints
41
41
41
I also tried different ways to initialize, but with no luck.
# my try #2
state_settings = StateSettings(
state = State(
z = 0,
angle = 0),
list_of_states = [State for i in range(some_number)]
)
for i in range(some_number):
state_settings.list_of_states[i].angle = i
or
# my try 3
from copy import deepcopy
state_settings = StateSettings
state_settings.list_of_states = [deepcopy(State) for i in range(some_number)]
state_settings.state = deepcopy(State)
for i in range(some_number):
state_settings.list_of_states[i].angle = i
My question, as far as I know, is not solved by answers such as Changing a single object within an array of objects changes all, even in a different array or List of Objects changes when the object that was input in the append() function changes.
There are some fundamental mistakes you have made in the code. Let me try to put some light on those first , using your lines of code
# my try #1
state_settings = StateSettings
What you did in the above line is that, you assigned the class StateSettings to state_settings variable. You never created an object here.
#create a list of State objects to be used later
state_settings.list_of_states = [State for i in range(some_number)]
What you did here is also same, created a list of State class references, not objects. So, all the values in list are same.
state_settings.state = State
What you did here, is set an attribute state to StateSettings class , not the object.
for i in range(some_number):
state_settings.list_of_states[i].angle = i
What you did here, set an attribute angle the class State. Since all values in the list are same State references, everywhere value will be same
To summarize the above said issues,
When you assign an attribute to the class name, attribute gets added to the class itself. Any where you have a reference to that class will have the same attribute value.
When you create an object and then set an attribute on the object, the attribute lies only in that object. Its not reflected on other objects created.
A simple update on the code you wrote is below, which I guess works like you want.
from typing import List
class State:
# think of State as some kind of coordinates
# Use default values, so you dont need to provide a value in init
def __init__(self, z: float = None, angle: float = None):
self.z = z
self.angle = angle
class ListOfStates:
# this should be an object with a list containing DIFFERENT (unlinked) State objects
# Use default values, so you dont need to provide a value in init
def __init__(self, list_of_states: List[State] = None):
self.list_of_states = list_of_states
class StateSettings:
# a bigger object to encapsulate previous objects
# Use default values, so you dont need to provide a value in init
def __init__(self, state: State = None, list_of_states: ListOfStates = None):
self.state = state
self.list_of_states = list_of_states
some_number = 42
# my try #1
state_settings = StateSettings()
# create a list of State objects to be used later
state_settings.list_of_states = [State() for i in range(some_number)]
state_settings.state = State()
for i in range(some_number):
state_settings.list_of_states[i].angle = i

instantiating an object with a dictionary field prints empty

In the following code, I'm trying to print a dictionary that contains the node gateway id as well as the node id as {1:gatewayid, 2:deviceNodeId}, however when instantiating the gateways and the nodes with the dictionary field, it prints an empty dictionary. I'll greatly appreciate your help please:
class Node():
def __init__(self,id, gatewayIds):
self.id=id
self.gatewayIds={}
class LevelNNode(Node):
def __init__(self, id, gatewayIds):
super(LevelNNode,self).__init__(id, gatewayIds)
self.balance=0
totalLevels=2
Dn = 10
Gn = 2
NODES = []
GATEWAYIDS = [chr(x+97) for x in range(Gn)]
if totalLevels==2:
for i in GATEWAYIDS: # Create all the gateways
otherGatewayIds = GATEWAYIDS.copy()
otherGatewayIds.remove(i)
NODES.append(LevelNNode(i, {1:i}))
deviceNodeId = 1 # Create the nodes for the level below
for i in GATEWAYIDS:
for j in range(Dn):
NODES.append(Node(deviceNodeId, {1:i, 2:deviceNodeId}))
deviceNodeId += 1
for x in NODES:
print (x.id, x.gatewayIds)
That's because you set the instance variable equal to {} and don't actually use the parameter you pass in. In the Node's __init__ try this: self.gatewayIds=gatewayIds

how to add methods to class attributes in python?

I'm creating a custom class to store information about a CFD simulation results.
Right now the way it is set up is that it instantiates an empty class object, then used a method called load_mesh which calls an external function to read all the information about the mesh, and return a dictionary of all the information. The load_mesh method then assigns a bunch of class attributes from the values in the dictionary.
The problem is that I am planning to store alot more information than just the mesh, and I dont want to have like 1000 attributes to my class object. I want to store then in appropriate containers(?) that each have their own methods.
For example, my code looks like this currently (some stuff omitted that's unnecessary):
class CFD():
def __init__(self, infile=None):
self.file = infile
def load_mesh(self):
mesh = load_cfd_mesh(self) #calls outside function to load mesh info, uses self.file, returns dict
self.proj = mesh['proj']
self.static_items = mesh['static_items']
self.nnodes = mesh['nnodes']
self.node_coords = mesh['node_coords']
self.node_codes = mesh['node_codes']
self.nelements = mesh['nelements']
self.element_types = mesh['element_types_str']
self.node_connectivity = mesh['node_connectivity']
self.element_node_ids = mesh['element_node_ids']
self.element_coords = mesh['element_coords']
self.element_elevs = mesh['element_elevs']
self.horizontal_units = mesh['horizontal_units']
self.vertical_units = mesh['vertical_units']
test = CFD('testfile.txt') #instantiate
test.load_mesh() #load mesh information to attributes
Now, I can access any of the mesh information by doing:
test.proj
self.nnodes
self.coords
etc...
But want I want to do is store all of this information in test.mesh, where test.mesh has all of these attributes but also has the method test.mesh.load().
I THINK I can do something like this:
class CFD():
def __init__(self, infile=None):
self.file = infile
self.mesh = None
def load_mesh(self):
mesh = load_cfd_mesh(self) #calls outside function to load mesh info, uses self.file, returns dict
setattr(self.mesh, 'proj', mesh['proj'])
#etc....
then I'd be able to do:
test = CFD('testfile.txt') #instantiate
test.load_mesh() #load mesh information to attributes
test.mesh.proj
But I can't figure out how to add the load_mesh method to self.mesh?
How is it possible to achieve the following way of doing this:
test = CFD('testfile.txt') #instantiate
test.mesh.load() #load mesh information to attributes
test.mesh.proj
Do I have to define another class within the main class? Like class mesh(self):
Also, if my proposed way of adding attributes to self.mesh doesn't make sense..please help!
I think you might be looking for something like a property to lazily load the mesh when needed – I don't really see why there'd be an "empty" mesh object you explicitly have to .load():
class Mesh:
def __init__(self, filename):
mesh = load_cfd_mesh(filename)
self.proj = mesh["proj"]
self.static_items = mesh["static_items"]
# ...
class CFD:
def __init__(self, filename):
self.filename = filename
self._mesh = None
#property
def mesh(self):
if not self._mesh:
self._mesh = Mesh(self.filename)
return self._mesh
test = CFD("testfile.txt")
print(test.mesh.proj)
You can do that with an inner class (below is a simplified code for demonstrating):
class CFD:
class Mesh:
def __init__(self, file):
self._file = file
def load_mesh(self):
# implement here your own code...
print("loading from file", self._file)
self.proj = "PROJ"
def __init__(self, file):
self.mesh = self.__class__.Mesh(file)

Python libtcod: Object description feature error

I'm working my way through the libtcod python tutorial, I've decided to mess around with some of the code to make it more unique today, and decided to start off with a feature to allow the player to hover the mouse over an object and press 'd' for a description of that object.
I'm currently running into an attribute error: 'str' object has no attribute 'describe' line 657. I've tried many different things but notihng seems to work, unfortunately my level of understanding is pretty limited right now so I can't figure out what's going wrong.
Here are the relevant classes and functions:
class Object:
#this is a generic object: the player, a monster, an item, the stairs...
#it's always represented by a character on screen.
def __init__(self, x, y, char, name, color, blocks=False, fighter=None, ai=None, item=None, description=None):
self.x = x
self.y = y
self.char = char
self.name = name
self.color = color
self.blocks = blocks
self.fighter = fighter
if self.fighter: #let the fighter component know who owns it
self.fighter.owner = self
self.ai = ai
if self.ai: #let the ai component know who owns it
self.ai.owner = self
self.item = item
if self.item: #let the item component know who owns it, like a bitch
self.item.owner = self
self.description = self
if self.description: #let the description component know who owns it
self.description.owner = self
def describe(self):
#describe this object
message(self.description, libtcod.white)
def handle_keys():
global keys;
if key_char == 'd':
#show the description menu, if an item is selected, describe it.
chosen_object = description_menu('Press the key next to an object to see its description.\n')
if chosen_object is not None:
chosen_object.describe()
return 'didnt-take-turn'
def description_menu(header):
global mouse
#return a string with the names of all objects under the mouse
(x, y) = (mouse.cx, mouse.cy)
#create a list with the names of all objects at the mouse's coordinates and in FOV
names = [obj.name for obj in objects if obj.x == x and obj.y == y and libtcod.map_is_in_fov(fov_map, obj.x, obj.y)]
names = ', '.join(names) #join the names, seperated by commas
return names.capitalize()
#show a menu with each object under the mouse as an option
if len(names) == 0:
options = ['There is nothing here.']
else:
options = [item.name for item in names]
index = menu(header, options, INVENTORY_WIDTH)
#if an item was chosen, return it
if index is None or len(names) == 0: return None
return names[index].description
Any help would be much appreciated!
The function description_menu() has the following return
names[index].description
This is a string member that belongs to Object.
When you say
chosen_object.describe()
You are calling the describe() method, but that belongs to the Object class, not a string (hence the attribute error: 'str' object has no attribute 'describe'). You would have to have description_menu() return the Object instead of just the name of it.

Python: referencing class object list of lists

I am fairly new to python. I have tried to define a class, I then want to create an instance from a file, then refer to specific pieces of it, but cannot seem to. This is Python 3.3.0
Here's the class....
class Teams():
def __init__(self, ID = None, Team = None, R = None, W = None, L = None):
self._items = [ [] for i in range(5) ]
self.Count = 0
def addTeam(self, ID, Team, R=None, W = 0, L = 0):
self._items[0].append(ID)
self._items[1].append(Team)
self._items[2].append(R)
self._items[3].append(W)
self._items[4].append(L)
self.Count += 1
def addTeamsFromFile(self, filename):
inputFile = open(filename, 'r')
for line in inputFile:
words = line.split(',')
self.addTeam(words[0], words[1], words[2], words[3], words[4])
def __len__(self):
return self.Count
Here's the code in Main
startFileName = 'file_test.txt'
filename = startFileName
###########
myTestData = Teams()
myTestData.addTeamsFromFile(startFileName)
sample data in file
100,AAAA,106,5,0
200,BBBB,88,3,2
300,CCCC,45,1,4
400,DDDD,67,3,2
500,EEEE,90,4,1
I think I am good to here (not 100% sure), but now how do I reference this data to see... am i not creating the class correctly? How do I see if one instance is larger than another...
ie, myTestData[2][2] > myTestData[3][2] <----- this is where I get confused, as this doesn't work
Why don't you create a Team class like this :
class Team():
def __init__(self, ID, Team, R=None, W = 0, L = 0)
# set up fields here
Then in Teams
class Teams():
def __init__(self):
self._teams = []
def addTeam (self, ID, Team, R=None, W = 0, L = 0)
team = Team (ID, Team, R=None, W = 0, L = 0)
self._teams.append (team)
Now If i got it right you want to overwrite the > operator's behaviour.
To do that overload __gt__(self, other) [link]
So it will be
class Team ():
# init code from above for Team
def __gt__ (self, otherTeam):
return self.ID > otherTeam.ID # for example
Also be sure to convert those strings to numbers because you compare strings not numbers. Use int function for that.
The immediate problem you're running into is that your code to access the team data doesn't account for your myTestData value being an object rather than a list. You can fix it by doing:
myTestData._items[2][2] > myTestData._items[3][2]
Though, if you plan on doing that much, I'd suggest renaming _items to something that's obviously supposed to be public. You might also want to make the addTeamsFromFile method convert some of the values it reads to integers (rather than leaving them as strings) before passing them to the addTeam method.
An alternative would be to make your Teams class support direct member access. You can do that by creating a method named __getitem__ (and __setitem__ if you want to be able to assign values directly). Something like:
def __getitem__(self, index):
return self._items[index]
#Aleksandar's answer about making a class for the team data items is also a good one. In fact, it might be more useful to have a class for the individual teams than it is to have a class containing several. You could replace the Teams class with a list of Team instances. It depends on what you're going to be doing with it I guess.

Categories