{"id":840,"date":"2018-08-16T03:48:03","date_gmt":"2018-08-16T00:48:03","guid":{"rendered":"http:\/\/kusuaks7\/?p=445"},"modified":"2021-05-11T13:58:38","modified_gmt":"2021-05-11T13:58:38","slug":"a-concrete-application-of-topological-data-analysis","status":"publish","type":"post","link":"https:\/\/www.experfy.com\/blog\/bigdata-cloud\/a-concrete-application-of-topological-data-analysis\/","title":{"rendered":"A concrete application of Topological Data Analysis"},"content":{"rendered":"<p><strong><em>Ready to learn Data Science? Browse&nbsp;<a href=\"https:\/\/www.experfy.com\/training\/tracks\/data-science-training-certification\">Data Science Training and Certification<\/a> courses developed by industry thought leaders and Experfy in Harvard Innovation Lab.<\/em><\/strong><\/p>\n<p id=\"e924\" name=\"e924\">Today, I will present a Machine Learning application of&nbsp;<strong>Topological Data Analysis&nbsp;<\/strong>(TDA), a rapidly evolving field of data science which makes use of topology to improve data analysis.&nbsp;<\/p>\n<h3 id=\"2bff\" name=\"2bff\"><strong>Great! Wait&hellip; what is&nbsp;TDA?<\/strong><\/h3>\n<p id=\"eb20\" name=\"eb20\">I will start by briefly recalling the basics of TDA. The interested reader might also want to take a look at&nbsp;<a data-href=\"https:\/\/towardsdatascience.com\/from-tda-to-dl-d06f234f51d\" href=\"https:\/\/towardsdatascience.com\/from-tda-to-dl-d06f234f51d\" target=\"_blank\" rel=\"noopener noreferrer\">other<\/a>&nbsp;<a data-href=\"https:\/\/towardsdatascience.com\/tda-to-rule-them-all-tomato-clustering-878e03394a1\" href=\"https:\/\/towardsdatascience.com\/tda-to-rule-them-all-tomato-clustering-878e03394a1\" target=\"_blank\" rel=\"noopener noreferrer\">stories<\/a>&nbsp;(and all references therein) for further details.<\/p>\n<p id=\"c0e1\" name=\"c0e1\">TDA is a mathematically grounded theory which aims at characterizing data using its&nbsp;<em>topology<\/em>, which<em>&nbsp;<\/em>is done by computing features of topological nature. The most common one is the&nbsp;<em>persistence diagram<\/em>, which takes the form of a set of points in the plane above the diagonal.<\/p>\n<p name=\"c0e1\" style=\"text-align: center;\"><img decoding=\"async\" data-image-id=\"1*Zh6r_c5SLHqFDK9NgpqOew.png\" src=\"https:\/\/cdn-images-1.medium.com\/max\/800\/1*Zh6r_c5SLHqFDK9NgpqOew.png\" style=\"width: 320px; height: 240px;\" \/><\/p>\n<p name=\"c0e1\" style=\"text-align: center;\">Example of persistence diagram computed with the library Gudhi. Source:&nbsp;<a data-href=\"http:\/\/gudhi.gforge.inria.fr\/python\/latest\/persistence_graphical_tools_user.html\" href=\"http:\/\/gudhi.gforge.inria.fr\/python\/latest\/persistence_graphical_tools_user.html\" rel=\"noopener noreferrer\" target=\"_blank\">http:\/\/gudhi.gforge.inria.fr\/python\/latest\/persistence_graphical_tools_user.html<\/a><\/p>\n<p name=\"a7dd\">Each of such point represents a&nbsp;<em>topological feature<\/em>&nbsp;(such as a connected component, a hole or a cavity) of the data. Moreover, the distance of the point to the diagonal act as an indicator of the importance of the corresponding feature, the usual interpretation being that points close to the diagonal are likely due to noise.<\/p>\n<p id=\"884d\" name=\"884d\">The computation of such diagrams requires a&nbsp;<em>filtration<\/em>, that is, a sequence of growing spaces: each space in the sequence is included in the next one. For instance, given a point cloud, a possible filtration would be to compute unions of balls centered on the points with a sequence of increasing radii.<\/p>\n<figure id=\"5dfd\" name=\"5dfd\">\n<p><img decoding=\"async\" data-image-id=\"1*OcVNTQZSEAq-cus05-TnBA.jpeg\" data-is-featured=\"true\" src=\"https:\/\/cdn-images-1.medium.com\/max\/800\/1*OcVNTQZSEAq-cus05-TnBA.jpeg\" style=\"width: 700px; height: 277px;\" \/><\/p>\n<\/figure>\n<p id=\"0e1f\" name=\"0e1f\" style=\"text-align: center;\">Example of union of balls filtration.<\/p>\n<p name=\"0e1f\">The idea is then, for each space in the sequence, to record whether a topological feature is either&nbsp;<em>created<\/em>&nbsp;or&nbsp;<em>destroyed&nbsp;<\/em>in that space. For instance, if we consider the union of balls filtration, it may happen that, for some radius, the ball union contains a hole, which persists for some time until it eventually gets filled in when the balls have a sufficiently large radius, which is exactly what happens in the filtration displayed above. These radii can then be used as coordinates to create a point in the plane which represents this hole.<\/p>\n<h3 id=\"c84b\" name=\"c84b\"><strong>Sounds good but&hellip; I want an application!<\/strong><\/h3>\n<p id=\"67e2\" name=\"67e2\">Persistence diagrams have many applications in various fields of data analysis. In this note, I will present a visual application in geometry processing, namely&nbsp;<strong>3D shape segmentation<\/strong>.<\/p>\n<p id=\"2368\" name=\"2368\">3D shapes are usually stored as a set of points, edges and triangles in a computer. The goal of segmentation is to provide labels for each point of each shape. For instance, if you are given a bunch of 3D shapes representing humans, the goal of segmentation is to successfully assign for each point the body part to which it belongs (&ldquo;torso&rdquo;, &ldquo;arm&rdquo;, &ldquo;leg&rdquo;&hellip;).<\/p>\n<p name=\"2368\" style=\"text-align: center;\"><img decoding=\"async\" data-image-id=\"1*Kt7sRWUPovkEoEIlqXebzA.png\" src=\"https:\/\/cdn-images-1.medium.com\/max\/800\/1*Kt7sRWUPovkEoEIlqXebzA.png\" style=\"width: 540px; height: 450px;\" \/><\/p>\n<p id=\"caa7\" name=\"caa7\" style=\"text-align: center;\">Segmented 3D human shapes. The different colors correspond to the different labels or parts. Source:&nbsp;<a data-href=\"https:\/\/people.cs.umass.edu\/~kalo\/papers\/LabelMeshes\/\" href=\"https:\/\/people.cs.umass.edu\/~kalo\/papers\/LabelMeshes\/\" rel=\"noopener noreferrer\" target=\"_blank\">https:\/\/people.cs.umass.edu\/~kalo\/papers\/LabelMeshes\/<\/a><\/p>\n<p name=\"caa7\">The difficulty of this problem lies in the fact that you are only given point coordinates, which are poor features. Indeed, it is hopeless to characterize a point with its coordinates since they depend on the embedding, or pose, of the 3D shape. Think for instance of two human shapes, one of them having its right hand raised and not the other; the humans are identical, only their poses differ. Then, the right hand points of the two shapes will differ a lot, even though they share the same label.<\/p>\n<h3 id=\"30f4\" name=\"30f4\"><strong>TDA to the&nbsp;rescue!<\/strong><\/h3>\n<p id=\"78ee\" name=\"78ee\">This is where persistence diagrams come into play. Thanks to their topological nature, persistence diagrams are&nbsp;<em>intrinsic<\/em>, meaning that they do not depend on the embedding, or pose, of the 3D shapes. Hence, they are good candidates for point features. To do this we thus need to define an intrinsic filtration.<\/p>\n<p id=\"d15d\" name=\"d15d\">This can be achieved with<em>&nbsp;geodesic distances<\/em>. The geodesic distance between two points on a 3D shape is the length of the shortest path on the shape between these two points. You can think of it as the length of the path that an ant would walk if it had to go from the first point to the second one. This distance is obviously intrinsic since the path that the ant would walk is independent from the pose of the 3D shape.<\/p>\n<p name=\"d15d\" style=\"text-align: center;\"><img decoding=\"async\" data-image-id=\"1*Y5vEjkGEG1aMu6VUePppRQ.png\" src=\"https:\/\/cdn-images-1.medium.com\/max\/800\/1*Y5vEjkGEG1aMu6VUePppRQ.png\" \/><\/p>\n<p id=\"8aef\" name=\"8aef\" style=\"text-align: center;\">Example of geodesic distances computed for various pair of points on a 3D camel shape. Source:&nbsp;<a data-href=\"http:\/\/www.numerical-tours.com\/matlab\/shapes_2_bendinginv_3d\/\" href=\"http:\/\/www.numerical-tours.com\/matlab\/shapes_2_bendinginv_3d\/\" rel=\"noopener noreferrer\" target=\"_blank\">http:\/\/www.numerical-tours.com\/matlab\/shapes_2_bendinginv_3d\/<\/a><\/p>\n<p name=\"8aef\">Geodesic distances can then be used to define<em>&nbsp;geodesic balls.<\/em>&nbsp;A geodesic ball with radius&nbsp;<em>r<\/em>&nbsp;&gt; 0 and centered on a point&nbsp;<em>x<\/em>&nbsp;is simply the set of points of the shape whose geodesic distance to&nbsp;<em>x<\/em>&nbsp;is less or equal than&nbsp;<em>r<\/em>. Again, by making&nbsp;<em>r<\/em>increase from 0 to infinity, we make the geodesic ball grow from the singleton&nbsp;<em>{x}<\/em>&nbsp;to the whole shape itself, which gives us an intrinsic filtration. Now, to compute the corresponding persistence diagram, we record the radii for which topological events occurred in the ball and use them as coordinates. In the case of 3D shapes, the topological events are quite limited: since 3D shapes are connected surfaces, their intrinsic dimension is 2 (indeed, a 3D shape locally looks like a flat plane), and the only topological events that may occur is the appearance or filling of holes in the ball. For example, take a look at the filtration displayed below on a 3D hand shape.<\/p>\n<figure id=\"2936\" name=\"2936\">\n<p><img decoding=\"async\" data-image-id=\"1*fkY641tzF9Drd8g9w3HDIA.jpeg\" src=\"https:\/\/cdn-images-1.medium.com\/max\/800\/1*fkY641tzF9Drd8g9w3HDIA.jpeg\" style=\"width: 700px; height: 193px;\" \/><\/p>\n<\/figure>\n<p id=\"1897\" name=\"1897\" style=\"text-align: center;\">Example of geodesic ball filtration computed for a point located on the palm of a 3D hand&nbsp;shape.<\/p>\n<p name=\"1897\">The growing geodesic ball is shown in red while the remaining of the shape is in blue. For the first three radii, the geodesic ball has no interesting topology: it looks just like a disc. However, for the fourth radius, each of the five fingers created a hole in the geodesic ball: five topological events appeared. They persist through the fifth radius, and eventually get filled in for the sixth radius. The corresponding persistence diagram, that I display below, thus contains five points.<\/p>\n<p name=\"1897\" style=\"text-align: center;\"><img decoding=\"async\" data-image-id=\"1*BfkiMDSdB7IVLt8dI1ktAQ.jpeg\" src=\"https:\/\/cdn-images-1.medium.com\/max\/800\/1*BfkiMDSdB7IVLt8dI1ktAQ.jpeg\" \/><\/p>\n<p id=\"8a67\" name=\"8a67\" style=\"text-align: center;\">Each point of the persistence diagram corresponds to a specific finger of the&nbsp;shape.<\/p>\n<p name=\"8a67\">What makes things more interesting is that if I apply the same process on a point located on another part of the shape, then the diagram is going to be different. Let us for example consider a point located on the middle finger:<\/p>\n<figure id=\"d31a\" name=\"d31a\">\n<p><img decoding=\"async\" data-image-id=\"1*OTL5gUmyEbX5b9nREjvyPQ.jpeg\" src=\"https:\/\/cdn-images-1.medium.com\/max\/800\/1*OTL5gUmyEbX5b9nREjvyPQ.jpeg\" style=\"width: 700px; height: 206px;\" \/><\/p>\n<\/figure>\n<p id=\"b1b4\" name=\"b1b4\" style=\"text-align: center;\">Geodesic ball filtration computed for a point located on the middle&nbsp;finger.<\/p>\n<p name=\"b1b4\">All fingers will again create holes in the geodesic ball, but at different radii. For instance, the hole corresponding to the middle finger appeared and got filled in much earlier than for the first filtration. In the persistence diagram, the corresponding point is thus located farther apart from the other points.<\/p>\n<p name=\"b1b4\" style=\"text-align: center;\"><img decoding=\"async\" data-image-id=\"1*SnY9MQxBD-77vcXVyag-lQ.jpeg\" src=\"https:\/\/cdn-images-1.medium.com\/max\/800\/1*SnY9MQxBD-77vcXVyag-lQ.jpeg\" \/><\/p>\n<p id=\"a6fa\" name=\"a6fa\" style=\"text-align: center;\">The point corresponding to the middle finger (pink) is far from the&nbsp;others.<\/p>\n<p name=\"a6fa\">Generally speaking, the persistence diagram points have different configurations depending on the location, or part, to which the 3D shape point (which was used to compute the diagram) belongs. This illustrates the fact that persistence diagrams are accurate descriptors for segmentation. I implemented code for the computation of such diagrams: see&nbsp;<a data-href=\"https:\/\/github.com\/MathieuCarriere\/local-persistence-with-UF\" href=\"https:\/\/github.com\/MathieuCarriere\/local-persistence-with-UF\" rel=\"noopener noreferrer\" target=\"_blank\">my Github<\/a>&nbsp;if you want to give it a try.<\/p>\n<h3 id=\"dff6\" name=\"dff6\"><strong>Time to&nbsp;learn!<\/strong><\/h3>\n<p id=\"e56f\" name=\"e56f\">Finally! Now that we have a good feature, or descriptor, of the points, we are ready to do Machine Learning (ML).<\/p>\n<p id=\"693c\" name=\"693c\">Or are we?<\/p>\n<p id=\"12b5\" name=\"12b5\">A big issue of persistence diagrams is their non structured form: persistence diagrams are sets which may contain different number of points. They are not as easy to handle as traditional Euclidean vectors, which are the common food for ML algorithms. This is why a huge effort is currently devoted in the TDA community to derive ways to process persistence diagrams in ML. As of today, the two main approaches are either to compute vectors out of diagrams (such as persistence images or landscapes), or to define kernels on diagrams and use kernelized ML algorithms (such as PCA or SVM). See&nbsp;<a data-href=\"https:\/\/towardsdatascience.com\/from-tda-to-dl-d06f234f51d\" href=\"https:\/\/towardsdatascience.com\/from-tda-to-dl-d06f234f51d\" target=\"_blank\" rel=\"noopener noreferrer\">this story<\/a>&nbsp;for more details.<\/p>\n<p id=\"e645\" name=\"e645\">I implemented most of this approaches in a python package that is&nbsp;<strong>scikit-learn<\/strong>&nbsp;compatible. Again, I refer the interested reader to&nbsp;<a data-href=\"https:\/\/github.com\/MathieuCarriere\/sklearn_tda\" href=\"https:\/\/github.com\/MathieuCarriere\/sklearn_tda\" rel=\"noopener noreferrer\" target=\"_blank\">my Github<\/a>. Thanks to this package, all methods can be compared and used in a big cross validation procedure. In&nbsp;<a data-href=\"https:\/\/github.com\/MathieuCarriere\/sklearn_tda\/blob\/master\/data\/3DSegTDA.ipynb\" href=\"https:\/\/github.com\/MathieuCarriere\/sklearn_tda\/blob\/master\/data\/3DSegTDA.ipynb\" rel=\"noopener noreferrer\" target=\"_blank\">my notebook<\/a>, I used this to perform segmentation of a bunch of 3D surfaces representing airplanes of various size and shape (see sample of code below). Final accuracy can go up to 90%, which is quite good given the difficulty of the task!<\/p>\n<p id=\"9575\" name=\"9575\"><span style=\"font-family:courier new,courier,monospace;\"><em><span style=\"background-color:#F0F8FF;\"># Definition of pipeline<\/span><\/em><br \/>\n<span style=\"background-color:#F0F8FF;\">pipe = Pipeline([(&quot;Separator&quot;, tda.DiagramSelector(limit=np.inf, point_type=&quot;finite&quot;)),<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">(&quot;Rotator&quot;, tda.DiagramPreprocessor(scaler=tda.BirthPersistenceTransform())),<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">(&quot;TDA&quot;, tda.PersistenceImage()),<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">(&quot;Estimator&quot;, SVC())])<\/span><\/span><\/p>\n<p name=\"f290\"><span style=\"font-family:courier new,courier,monospace;\"><em><span style=\"background-color:#F0F8FF;\"># Parameters of pipeline. This is the place where you specify the methods you want to use to handle diagrams<\/span><\/em><br \/>\n<span style=\"background-color:#F0F8FF;\">param = [{&quot;Rotator__use&quot;: [False],<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;TDA&quot;: [tda.SlicedWasserstein()], <\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;TDA__bandwidth&quot;: [0.1, 1.0],<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;TDA__num_directions&quot;: [20],<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;Estimator&quot;: [SVC(kernel=&quot;precomputed&quot;)]},<\/span><\/span><\/p>\n<p name=\"f290\"><span style=\"font-family:courier new,courier,monospace;\"><span style=\"background-color:#F0F8FF;\">&nbsp;{&quot;Rotator__use&quot;: [False],<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;TDA&quot;: [tda.PersistenceWeightedGaussian()], <\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;TDA__bandwidth&quot;: [0.1, 1.0],<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;TDA__weight&quot;: [<\/span><strong><span style=\"background-color:#F0F8FF;\">lambda<\/span><\/strong><span style=\"background-color:#F0F8FF;\"> x: np.arctan(x[1]-x[0])], <\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;Estimator&quot;: [SVC(kernel=&quot;precomputed&quot;)]},<\/span><\/span><\/p>\n<p name=\"f290\"><span style=\"font-family:courier new,courier,monospace;\"><span style=\"background-color:#F0F8FF;\">&nbsp;{&quot;Rotator__use&quot;: [True],<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;TDA&quot;: [tda.PersistenceImage()], <\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;TDA__resolution&quot;: [ [5,5], [6,6] ],<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;TDA__bandwidth&quot;: [0.01, 0.1, 1.0, 10.0],<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;Estimator&quot;: [SVC()]},<\/span><\/span><\/p>\n<p name=\"f290\"><span style=\"font-family:courier new,courier,monospace;\"><span style=\"background-color:#F0F8FF;\">&nbsp;{&quot;Rotator__use&quot;: [False],<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;TDA&quot;: [tda.Landscape()], <\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;TDA__resolution&quot;: [1000],<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;Estimator&quot;: [RandomForestClassifier()]},<\/span><\/span><\/p>\n<p name=\"f290\"><span style=\"font-family:courier new,courier,monospace;\"><span style=\"background-color:#F0F8FF;\">&nbsp;{&quot;Rotator__use&quot;: [False],<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;TDA&quot;: [tda.WassersteinDistance()], <\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;TDA__wasserstein&quot;: [1],<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;TDA__delta&quot;: [0.1], <\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">&quot;Estimator&quot;: [KNeighborsClassifier(metric=&quot;precomputed&quot;)]}<\/span><br \/>\n<span style=\"background-color:#F0F8FF;\">]<\/span><\/span><\/p>\n<h3 id=\"c133\" name=\"c133\"><strong>One final&nbsp;word&hellip;<\/strong><\/h3>\n<p id=\"fc1d\" name=\"fc1d\">Geometry processing is only one out of many possible applications of TDA. This field is very active since it connects different areas of mathematics from algebraic topology to computer science, and more and more people are becoming TDA enthusiasts. Do not miss the train!&nbsp;\ud83d\ude09<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Topological data&nbsp;analysis (TDA) is a mathematically grounded theory which aims at characterizing data using its&nbsp;topology, which&nbsp;is done by computing features of topological nature.&nbsp;This is why a huge effort is currently devoted in the TDA community to derive ways to process persistence diagrams in ML. Geometry processing is only one out of many possible applications of TDA. This field is very active since it connects different areas of mathematics from algebraic topology to computer science, and more and more people are becoming TDA enthusiasts.&nbsp;<\/p>\n","protected":false},"author":322,"featured_media":2653,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"content-type":"","footnotes":""},"categories":[187],"tags":[94],"ppma_author":[2019],"class_list":["post-840","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bigdata-cloud","tag-data-science"],"authors":[{"term_id":2019,"user_id":322,"is_guest":0,"slug":"mathieu-carriere","display_name":"Mathieu Carri\u00e8re","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/?s=96&d=mm&r=g","user_url":"","last_name":"Carri\u00e8re","first_name":"Mathieu","job_title":"","description":"Mathieu Carri&egrave;re holds PhD in Machine Learning and Topological Data Analysis. He focused on topological data analysis (TDA) in his research that resulted in several scientific articles in top conference proceedings and scientific journals."}],"_links":{"self":[{"href":"https:\/\/www.experfy.com\/blog\/wp-json\/wp\/v2\/posts\/840","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.experfy.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.experfy.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.experfy.com\/blog\/wp-json\/wp\/v2\/users\/322"}],"replies":[{"embeddable":true,"href":"https:\/\/www.experfy.com\/blog\/wp-json\/wp\/v2\/comments?post=840"}],"version-history":[{"count":1,"href":"https:\/\/www.experfy.com\/blog\/wp-json\/wp\/v2\/posts\/840\/revisions"}],"predecessor-version":[{"id":6285,"href":"https:\/\/www.experfy.com\/blog\/wp-json\/wp\/v2\/posts\/840\/revisions\/6285"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.experfy.com\/blog\/wp-json\/wp\/v2\/media\/2653"}],"wp:attachment":[{"href":"https:\/\/www.experfy.com\/blog\/wp-json\/wp\/v2\/media?parent=840"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.experfy.com\/blog\/wp-json\/wp\/v2\/categories?post=840"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.experfy.com\/blog\/wp-json\/wp\/v2\/tags?post=840"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.experfy.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=840"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}