Skip to content
This repository has been archived by the owner on Jan 13, 2024. It is now read-only.

Commit

Permalink
update kruskal algorithm to deal with duplicated points
Browse files Browse the repository at this point in the history
  • Loading branch information
sdpython committed Apr 18, 2016
1 parent de1fc5c commit 86b6fd8
Show file tree
Hide file tree
Showing 11 changed files with 339 additions and 58 deletions.
5 changes: 4 additions & 1 deletion _doc/sphinxdoc/source/specials/index_expose.rst
Expand Up @@ -25,4 +25,7 @@ qu'on peut résoudre grâce à un algorithme et un peu d'imagination.
tsp_kruskal
graph_distance
voisinage
image_synthese
image_synthese


Certains de ces algorithmes sont réutilisables: :ref:`l-almost_reusable`.
1 change: 1 addition & 0 deletions _doc/sphinxdoc/source/td_2a.rst
Expand Up @@ -145,6 +145,7 @@ Algorithmique ludique
* types de complexité : `force brute <http://fr.wikipedia.org/wiki/Recherche_exhaustive>`_,
`glouton <http://fr.wikipedia.org/wiki/Algorithme_glouton>`_, `dynamique <http://fr.wikipedia.org/wiki/Programmation_dynamique>`_
* :ref:`l-algoculture`
* :ref:`l-expose-explication`



Expand Down
2 changes: 1 addition & 1 deletion _unittests/ut_faq/data/american_cities.txt
@@ -1,4 +1,4 @@
Name,Latitude,Longitude,City,States
Name,Latitude,Longitude,City,State
[ANB],33.58,85.85,Anniston,AL
[AUO],32.67,85.44,Auburn,AL
[BHM],33.57,86.75,Birmingham,AL
Expand Down
99 changes: 99 additions & 0 deletions _unittests/ut_faq/test_faq_matplotlib_video.py
@@ -0,0 +1,99 @@
"""
@brief test log(time=7s)
"""

import sys
import os
import unittest
import pandas
import matplotlib.pyplot as plt


try:
import src
except ImportError:
path = os.path.normpath(
os.path.abspath(
os.path.join(
os.path.split(__file__)[0],
"..",
"..")))
if path not in sys.path:
sys.path.append(path)
import src

try:
import pyquickhelper as skip_
except ImportError:
path = os.path.normpath(
os.path.abspath(
os.path.join(
os.path.split(__file__)[0],
"..",
"..",
"..",
"pyquickhelper",
"src")))
if path not in sys.path:
sys.path.append(path)
import pyquickhelper as skip_

from pyquickhelper.loghelper import fLOG
from pyquickhelper.pycode import get_temp_folder
from src.ensae_teaching_cs.faq.faq_matplotlib import graph_cities
from src.ensae_teaching_cs.special import tsp_kruskal_algorithm, distance_haversine


class TestFaqMatplotlibVideo(unittest.TestCase):

def test_american_cities(self):
fLOG(
__file__,
self._testMethodName,
OutputPrint=__name__ == "__main__")

def haversine(p1, p2):
return distance_haversine(p1[0], p1[1], p2[0], p2[1])

temp = get_temp_folder(__file__, "temp_matplotlib_video")
data = os.path.join(temp, "..", "data", "american_cities.txt")
df = pandas.read_csv(data)
df["Longitude"] = -df["Longitude"]
df = df[df.Latitude < 52]
df = df[df.Longitude > -130].copy()
fLOG(df.columns)
df = df.dropna()

if __name__ != "__main__":
df = df[:40].copy()
fLOG(df.shape)
# df["City"] = df["City"].apply(lambda v: filter.get(v, ""))
points = [(row[1], row[2], row[3])
for row in df.itertuples(index=False)]
fLOG("number of cities:", len(points))
trip = tsp_kruskal_algorithm(
points, distance=haversine, fLOG=fLOG, max_iter=10)

# trip
dftrip = pandas.DataFrame(
trip, columns=["Latitude", "Longitude", "City"])
save = os.path.join(temp, "trip.txt")
dftrip.to_csv(save, sep="\t", index=False)

# graph
for i in range(0, dftrip.shape[0]):
if i % 10 != 0:
dftrip.ix[i, "City"] = ""
fig, ax = plt.subplots(figsize=(32, 32))
ax = graph_cities(dftrip, ax=ax, markersize=3, linked=True, fLOG=fLOG,
fontcolor="red", fontsize='16', loop=True)
assert ax is not None
img = os.path.join(temp, "img.png")
fig.savefig(img)
assert os.path.exists(img)
if __name__ == "__main__":
fig.show()


if __name__ == "__main__":
unittest.main()
73 changes: 55 additions & 18 deletions src/ensae_teaching_cs/faq/faq_matplotlib.py
Expand Up @@ -62,15 +62,8 @@ def zoomable():
pass


def graph_ggplot_with_label(x,
y,
labels,
bar=True,
title=None,
figsize=(6, 4),
style=None,
ax=None,
**kwargs):
def graph_ggplot_with_label(x, y, labels, bar=True, title=None, figsize=(6, 4), style=None,
ax=None, **kwargs):
"""
creates a graph with matplotlib
Expand Down Expand Up @@ -264,12 +257,31 @@ def avoid_overlapping_dates(fig, **options):
fig.autofmt_xdate(**options)


def graph_cities(df, names=["Longitude", "Latitude", "City"], ax=None, linked=False, **params):
def graph_cities(df, names=["Longitude", "Latitude", "City"], ax=None, linked=False,
fLOG=None, loop=False, **params):
"""
plots the cities on a map
@param df dataframe
@param names names of the column Latitude, Longitude, City
@param ax existing ax
@param linked draw lines between points
@param loop add a final line to link the first point to the final one
@param fLOG logging function
@param params see below
@return ax
Other parameters:
* llcrnrlon, llcrnrlat, urcrnrlon, urcrnrlat
* resolution
* projection
* color, lake_color
* style
* markersize
* fontname, fontcolor, fontsize, fontweight, fontvalign
* slon, slat: space between meridians and parellels
"""
xx = list(df[names[0]])
yy = list(df[names[1]])
Expand All @@ -281,6 +293,15 @@ def graph_cities(df, names=["Longitude", "Latitude", "City"], ax=None, linked=Fa
minx, maxx = min(xx), max(xx)
miny, maxy = min(yy), max(yy)
avex, avey = numpy.mean(xx), numpy.mean(yy)
dx = (maxx - minx) / 10
dy = (maxy - miny) / 10
if fLOG:
fLOG("[graph_cities] Lon:[{0}, {1}] x Lat:[{2}, {3}] - mean={4}, {5} - linked={6}".format(minx,
maxx, miny, maxy, avex, avey, linked))
minx -= dx
maxx += dx
miny -= dy
maxy += dy

m = Basemap(llcrnrlon=params.get('llcrnrlon', minx),
llcrnrlat=params.get('llcrnrlat', miny),
Expand All @@ -293,30 +314,46 @@ def graph_cities(df, names=["Longitude", "Latitude", "City"], ax=None, linked=Fa
m.drawcountries()
m.fillcontinents(color=params.get('color', 'lightgrey'),
lake_color=params.get('lake_color', '#AAAAFF'))
m.drawparallels(numpy.arange(miny, maxy, 5.))
m.drawmeridians(numpy.arange(minx, maxx, 5.))
m.drawparallels(numpy.arange(miny, maxy, params.get('slat', 10.)))
m.drawmeridians(numpy.arange(minx, maxx, params.get('slon', 10.)))
m.drawmapboundary(fill_color=params.get('fill_color', 'aqua'))
# on ajoute Paris sur la carte

style = params.get('style', 'ro')
markersize = params.get('markersize', 6)
fontname = params.get('fontname', 'Arial')
fontsize = params.get('fontsize', '16')
fontcolor = params.get('fontcolor', 'black')
fontweight = params.get('fontweight', 'normal')
fontvalign = params.get('fontvalign', 'bottom')

if linked:
if "-" not in style:
style += "-"
xs, ys = [], []
i = 0
for lon, lat in zip(xx, yy):
x, y = m(lon, lat)
xs.append(x)
ys.append(y)
if nn[i] is not None and len(nn[i]) > 0:
plt.text(x, y, nn[i])
plt.text(x, y, nn[i], fontname=fontname, size=fontsize,
color=fontcolor, weight=fontweight,
verticalalignment=fontvalign)
i += 1
if loop:
xs.append(xs[0])
ys.append(ys[0])

m.plot(xs, yy, params.get('style', 'bo'),
markersize=params.get('markersize', 6))
m.plot(xs, ys, style, markersize=markersize)
else:
i = 0
for lon, lat in zip(xx, yy):
x, y = m(lon, lat)
m.plot(x, y, params.get('style', 'bo'),
markersize=params.get('markersize', 6))
m.plot(x, y, style, markersize=markersize)
if nn[i] is not None and len(nn[i]) > 0:
plt.text(x, y, nn[i])
plt.text(x, y, nn[i], fontname=fontname, size=fontsize,
color=fontcolor, weight=fontweight,
verticalalignment=fontvalign)
i += 1
return ax
23 changes: 23 additions & 0 deletions src/ensae_teaching_cs/special/__init__.py
@@ -0,0 +1,23 @@
"""
@file
@brief Shortcuts to special
.. _l-almost_reusable:
List of almost reusable algorithms implemented in this module
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* @see fn tsp_kruskal_algorithm: `TSP <https://en.wikipedia.org/wiki/Travelling_salesman_problem>`_
* @see fn draw_line: `Bresenham <https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm>`_ algorithm (line)
* @see fn draw_ellipse: `Bresenham <https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm>`_ algorithm (ellipse)
* @see fn distance_haversine: distance of `Haversine <https://en.wikipedia.org/wiki/Haversine_formula>`_
* @see fn bellman: shortest paths in a graph with `Bellman-Ford <http://fr.wikipedia.org/wiki/Algorithme_de_Bellman-Ford>`_
* @see fn connected_components: computes the `connected components <https://en.wikipedia.org/wiki/Connected_component_(graph_theory)>`_
* @see fn graph_degree: computes the degree of each node in a graph `degree <https://en.wikipedia.org/wiki/Degree_(graph_theory)>`_
* @see cl GraphDistance: computes a distance between two graphs (acyclic), see :ref:`l-graph_distance`
"""

from .tsp_kruskal import tsp_kruskal_algorithm
from .tsp_bresenham import draw_line, draw_ellipse
from .rues_paris import distance_haversine, bellman, connected_components, graph_degree
from .graph_distance import GraphDistance
16 changes: 7 additions & 9 deletions src/ensae_teaching_cs/special/graph_distance.py
Expand Up @@ -106,7 +106,7 @@ def Label(self):
return self.label


class Graph:
class GraphDistance:
"""
defines a graph
Expand All @@ -124,10 +124,8 @@ def get_list_of_vertices(graph):
vertices.sort()
return vertices

def __init__(self, edge_list, vertex_label={},
add_loop=False,
weight_vertex=1.,
weight_edge=1.):
def __init__(self, edge_list, vertex_label={}, add_loop=False,
weight_vertex=1., weight_edge=1.):
"""
constructor
Expand All @@ -143,7 +141,7 @@ def __init__(self, edge_list, vertex_label={},
self.edges = {}
self.labelBegin = "00"
self.labelEnd = "11"
vid = Graph.get_list_of_vertices(edge_list)
vid = GraphDistance.get_list_of_vertices(edge_list)
for u in vid:
self.vertices[u] = Vertex(
u, vertex_label.get(u, str(u)), weight_vertex)
Expand Down Expand Up @@ -191,7 +189,7 @@ def load_from_file(filename, add_loop):
vertex_label[g[0]] = g[1]
if len(vertex_label) == 0 or len(edge_list) == 0:
raise OSError("unable to parse file " + filename)
return Graph(edge_list, vertex_label, add_loop)
return GraphDistance(edge_list, vertex_label, add_loop)

def _private__init__(self, add_loop, weight_vertex, weight_edge):
if add_loop:
Expand Down Expand Up @@ -379,7 +377,7 @@ def common_paths(self, graph2,
function_mach_vertices, function_match_edges = \
self.get_matching_functions(
function_mach_vertices, function_match_edges)
g = Graph([])
g = GraphDistance([])
vfirst = Vertex(self.labelBegin, "%s-%s" % (self.labelBegin, self.labelBegin),
(self.vertices[self.labelBegin].weight +
graph2.vertices[self.labelBegin].weight) / 2)
Expand Down Expand Up @@ -707,7 +705,7 @@ def distance_matching_graphs_paths(self, graph2,
count_vertex_left, count_vertex_right = self.private_count_left_right(
reduction_vertex)

res_graph = Graph([])
res_graph = GraphDistance([])
doneVertex = {}
done_edge = {}

Expand Down

0 comments on commit 86b6fd8

Please sign in to comment.