{"id":3567,"date":"2020-03-03T07:00:30","date_gmt":"2020-03-03T12:00:30","guid":{"rendered":"https:\/\/blogs.mathworks.com\/loren\/?p=3567"},"modified":"2020-03-24T07:48:54","modified_gmt":"2020-03-24T12:48:54","slug":"matlab-speaks-python","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/loren\/2020\/03\/03\/matlab-speaks-python\/","title":{"rendered":"MATLAB Speaks Python"},"content":{"rendered":"<div class=\"content\"><!--introduction--><p>MATLAB is a great computing environment for engineers and scientists. MATLAB also provides access to general-purpose languages including C\/C++, Java, Fortran, .NET, and Python. Today's guest blogger, <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/profile\/authors\/951521\">Toshi Takeuchi<\/a>, would like to talk about <a href=\"https:\/\/www.mathworks.com\/products\/matlab\/matlab-and-python.html\">using MATLAB with Python<\/a>.<\/p><!--\/introduction--><h3>Contents<\/h3><div><ul><li><a href=\"#be17c082-ecc0-4b55-abb6-6db6a7140508\">Why Not Use Both?<\/a><\/li><li><a href=\"#f8a47991-c704-4c12-a7c9-fcb178689b6a\">Setting up Python in MATLAB<\/a><\/li><li><a href=\"#f91cb7d6-386b-4815-b2d3-6bd7cf2dc2b1\">Karate Club Dataset<\/a><\/li><li><a href=\"#d33a8b9e-0789-4589-9412-5128dae4e09e\">To Import or Not to Import<\/a><\/li><li><a href=\"#7cfcb146-9e87-4ead-b9f0-ad4ba34466fd\">Extracting Data from a Python Object<\/a><\/li><li><a href=\"#3a596381-988f-45e3-b7b1-614c1d1e52c3\">Handling a Python List and Tuple<\/a><\/li><li><a href=\"#2bc62295-e356-4e62-9fa0-380017a399b6\">Handling a Python Dict<\/a><\/li><li><a href=\"#5f223f12-7569-4a34-8596-de7ae6d4640c\">Visualizing the Graph in MATLAB<\/a><\/li><li><a href=\"#2b573019-e33f-440c-957a-78fb5979636b\">Passing Data from MATLAB to Python<\/a><\/li><li><a href=\"#8d14c1a3-42b0-44d7-9d49-07e95a8a1e78\">Community Detection with NetworkX<\/a><\/li><li><a href=\"#9d052ed3-37bb-405d-a70e-52475e7ba6a1\">Streamlining the Code<\/a><\/li><li><a href=\"#e092f08e-14d1-4d4a-86ea-33a416f76e88\">Summary<\/a><\/li><\/ul><\/div><h4>Why Not Use Both?<a name=\"be17c082-ecc0-4b55-abb6-6db6a7140508\"><\/a><\/h4><p>When we discuss languages, we often encounter a <a href=\"https:\/\/en.wikipedia.org\/wiki\/False_dilemma\">false choice<\/a> where you feel you must choose one or the other. In reality, you can often use both. Most of us don't work alone. As part of a larger team, your work is often part of a larger workflow that involves multiple languages. That's why MATLAB provides interoperability with other languages including Python. Your colleagues may want to take advantage of your MATLAB code, or you need to access Python-based functionality from your IT systems. MATLAB supports your workflow in both directions.<\/p><p>Today I would like to focus on <b>calling Python from MATLAB<\/b> to take advantage of some existing Python functionality within a MATLAB-based workflow.<\/p><p>In this post, we will see:<\/p><div><ul><li>How to import data from Python into MATLAB<\/li><li>How to pass data from MATLAB to Python<\/li><li>How to use a Python package in MATLAB<\/li><\/ul><\/div><h4>Setting up Python in MATLAB<a name=\"f8a47991-c704-4c12-a7c9-fcb178689b6a\"><\/a><\/h4><p>MATLAB supports <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/matlab_external\/system-and-configuration-requirements.html\">Python 2.7, 3.6 and 3.7<\/a> as of this writing (R2019b).  And here's another useful <a href=\"https:\/\/www.mathworks.com\/content\/dam\/mathworks\/mathworks-dot-com\/support\/sysreq\/files\/python-support.pdf\">link<\/a>.<\/p><p>I assume you already know how to install and manage Python environments and dependencies on your platform of choice, and I will not discuss it here because it is a complicated topic of its own.<\/p><p>Let's enable access to Python in MATLAB. You need to find the full path to your Python executable. Here is an example for Windows. On Mac and Linux, your operating system command may be different.<\/p><pre class=\"codeinput\">pe = pyenv;\r\n<span class=\"keyword\">if<\/span> pe.Status == <span class=\"string\">\"NotLoaded\"<\/span>\r\n    [~,exepath] = system(<span class=\"string\">\"where python\"<\/span>);\r\n    pe = pyenv(<span class=\"string\">'Version'<\/span>,exepath);\r\n<span class=\"keyword\">end<\/span>\r\n<\/pre><p>If that doesn't work, you can also just pass the path to your Python executable as string.<\/p><pre class=\"language-matlab\">pe =\r\npyenv(<span class=\"string\">'Version'<\/span>,<span class=\"string\">'C:\\Users\\username\\AppData\\Local\\your\\python\\path\\python.exe'<\/span>)\r\n<\/pre><pre class=\"codeinput\">myPythonVersion = pe.Version\r\npy.print(<span class=\"string\">\"Hello, Python!\"<\/span>)\r\n<\/pre><pre class=\"codeoutput\">myPythonVersion = \r\n    \"3.7\"\r\nHello, Python!\r\n<\/pre><h4>Karate Club Dataset<a name=\"f91cb7d6-386b-4815-b2d3-6bd7cf2dc2b1\"><\/a><\/h4><p><a title=\"http:\/\/www1.ind.ku.dk\/complexLearning\/zachary1977.pdf (link no longer works)\">Wayne Zachary<\/a> published a <a href=\"http:\/\/vlado.fmf.uni-lj.si\/pub\/networks\/data\/Ucinet\/UciData.htm#zachary\">dataset<\/a> that contains a social network of friendships between 34 members of a karate club at a US university in the 1970s. A dispute that erupted in this club eventually caused it to break up into two factions. We want to see if we can algorithmically predict how the club would break up based on its interpersonal relationships.<\/p><p>This dataset is included in <a href=\"https:\/\/networkx.github.io\/\">NetworkX<\/a>, a complex networks package for Python. We can easily get started by importing the dataset using this package.<\/p><p>I am using NetworkX 2.2. To check the package version in Python, you would typically use the version package attribute like this:<\/p><pre class=\"language-matlab\">&gt;&gt;&gt; networkx.__version__\r\n<\/pre><p>MATLAB doesn't support <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/matlab_external\/limitations-to-python-support.html\">class names or other identifiers starting with an underscore(_) character<\/a>. Instead, use the following to get the help content on the package, including its current version.<\/p><pre class=\"language-matlab\">&gt; py.help(<span class=\"string\">'networkx'<\/span>)\r\n<\/pre><h4>To Import or Not to Import<a name=\"d33a8b9e-0789-4589-9412-5128dae4e09e\"><\/a><\/h4><p>Typically, you do this at the start of your Python script.<\/p><pre class=\"language-matlab\">import <span class=\"string\">networkx<\/span> <span class=\"string\">as<\/span> <span class=\"string\">nx<\/span>\r\nG = nx.karate_club_graph()\r\n<\/pre><p>However, this is not recommended in MATLAB because the behavior of the <tt>import<\/tt> function in MATLAB is different from Python's.<\/p><p>The MATLAB way to call Python is to use <tt><b>py<\/b><\/tt>, followed by a package or method like this:<\/p><pre class=\"codeinput\">nxG = py.networkx.karate_club_graph();\r\n<\/pre><p>If you must use <tt>import<\/tt>, you can do it as follows:<\/p><pre class=\"language-matlab\">import <span class=\"string\">py.networkx.*<\/span>\r\nnxG = karate_club_graph();\r\n<\/pre><p>As you can see, it is hard to remember that we are calling a Python method when you omit <tt>py<\/tt>, which can be confusing when you start mixing MATLAB code and Python code within the same script.<\/p><h4>Extracting Data from a Python Object<a name=\"7cfcb146-9e87-4ead-b9f0-ad4ba34466fd\"><\/a><\/h4><p>The following returns the karate club dataset in a NetworkX graph object.<\/p><pre class=\"codeinput\">myDataType = class(nxG)\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'py.networkx.classes.graph.Graph'\r\n<\/pre><p>You can see the methods available on this object like this:<\/p><pre class=\"language-matlab\">methods(nxG)\r\n<\/pre><p>You can also see the properties of this object.<\/p><pre class=\"language-matlab\">properties(nxG)\r\n<\/pre><p>A NetworkX graph contains an <tt>edges<\/tt> property that returns an object called <tt>EdgeView<\/tt>.<\/p><pre class=\"codeinput\">edgeL = nxG.edges;\r\nmyDataType = class(edgeL)\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'py.networkx.classes.reportviews.EdgeView'\r\n<\/pre><p>To use this Python object in MATLAB, the first step is to convert the object into a core Python data type such as a Python <tt>list<\/tt>.<\/p><pre class=\"codeinput\">edgeL = py.list(edgeL);\r\nmyDataType = class(edgeL)\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'py.list'\r\n<\/pre><p>Now <tt>edgeL<\/tt> contains a Python <tt>list<\/tt> of node pairs stored as Python <tt>tuple<\/tt> elements. Each node pair represents an edge in the graph. Let's see the first 5 <tt>tuple<\/tt> values.<\/p><pre class=\"codeinput\">listContent = edgeL(1:5)\r\n<\/pre><pre class=\"codeoutput\">listContent = \r\n  Python list with no properties.\r\n\r\n    [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]\r\n<\/pre><h4>Handling a Python List and Tuple<a name=\"3a596381-988f-45e3-b7b1-614c1d1e52c3\"><\/a><\/h4><p>The Python way for handling a <tt>list<\/tt> or <tt>tuple<\/tt> typically looks like this, where you process individual elements in a loop.<\/p><pre class=\"language-matlab\"><span class=\"keyword\">for<\/span> i in <span class=\"string\">l:<\/span> <span class=\"string\">print<\/span> <span class=\"string\">i<\/span>             <span class=\"string\">#<\/span> <span class=\"string\">l<\/span> <span class=\"string\">is<\/span> <span class=\"string\">the<\/span> <span class=\"string\">list<\/span>\r\n<span class=\"keyword\">for<\/span> u, v <span class=\"string\">in<\/span> <span class=\"string\">t:<\/span> <span class=\"string\">print((u, v))<\/span>    <span class=\"string\">#<\/span> <span class=\"string\">t<\/span> <span class=\"string\">is<\/span> <span class=\"string\">the<\/span> <span class=\"string\">tuple<\/span>\r\n<\/pre><p>The MATLAB way is to use arrays instead. The Python <tt>list<\/tt> can be converted into a <tt>cell<\/tt> array.<\/p><pre class=\"codeinput\">edgeC = cell(edgeL);\r\nmyDataType = class(edgeC)\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'cell'\r\n<\/pre><p>This <tt>cell<\/tt> array contains Python <tt>tuple<\/tt> elements.<\/p><pre class=\"codeinput\">myDataType = class(edgeC{1})\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'py.tuple'\r\n<\/pre><p>The Python <tt>tuple<\/tt> can also be converted to a <tt>cell<\/tt> array. To convert the inner <tt>tuple<\/tt> elements, we can use <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/cellfun.html\"><tt>cellfun<\/tt><\/a>.<\/p><pre class=\"codeinput\">edgeC = cellfun(@cell, edgeC, <span class=\"string\">'UniformOutput'<\/span>, false);\r\nmyDataType = class(edgeC{1})\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'cell'\r\n<\/pre><p>The resulting nested <tt>cell<\/tt> array contains Python <tt>int<\/tt> values.<\/p><pre class=\"codeinput\">myDataType = class(edgeC{1}{1})\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'py.int'\r\n<\/pre><h4>Handling a Python Dict<a name=\"2bc62295-e356-4e62-9fa0-380017a399b6\"><\/a><\/h4><p>Now let's also extract the nodes from the dataset. We can follow the same steps as we did for the edges.<\/p><pre class=\"codeinput\">nodeL = py.list(nxG.nodes.data);\r\nnodeC = cell(nodeL);\r\nnodeC = cellfun(@cell, nodeC, <span class=\"string\">'UniformOutput'<\/span>, false);\r\n<\/pre><p>An inner <tt>cell<\/tt> array contains both Python <tt>int<\/tt> and <tt>dict<\/tt> elements.<\/p><pre class=\"codeinput\">cellContent = nodeC{1}\r\n<\/pre><pre class=\"codeoutput\">cellContent =\r\n  1&times;2 cell array\r\n    {1&times;1 py.int}    {1&times;1 py.dict}\r\n<\/pre><p>Python <tt>dict<\/tt> is a data type based on key-value pairs. In this case, the key is <tt>'club'<\/tt> and the value is <tt>'Mr. Hi'<\/tt>.<\/p><pre class=\"codeinput\">cellContent = nodeC{1}{2}\r\n<\/pre><pre class=\"codeoutput\">cellContent = \r\n  Python dict with no properties.\r\n\r\n    {'club': 'Mr. Hi'}\r\n<\/pre><p>Mr. Hi was a karate instructor at the club. The other value in the Python <tt>dict<\/tt> is <tt>'Officer'<\/tt>, and the officer was a leader of the club. They were the key individuals of the respective factions. The node attribute indicates which faction an individual node belongs to. In this case, Node 1 belonged to Mr. Hi's faction.<\/p><p>The Python way for handling a <tt>dict<\/tt> typically looks like this, where you process individual elements in a loop.<\/p><pre class=\"language-matlab\"><span class=\"keyword\">for<\/span> k, v <span class=\"string\">in<\/span> <span class=\"string\">d.items():<\/span>\r\n    print (k, v)\r\n<\/pre><p>Again, the MATLAB way is to use an array. The Python <tt>dict<\/tt> can be converted to a <tt>struct<\/tt> array.<\/p><pre class=\"codeinput\">nodeAttrs = cellfun(@(x) struct(x{2}), nodeC);\r\nmyDataType = class(nodeAttrs)\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'struct'\r\n<\/pre><p>We can extract the individual values into a <tt>string<\/tt> array. The club was evidently evenly divided between the factions.<\/p><pre class=\"codeinput\">nodeAttrs = arrayfun(@(x) string(x.club), nodeAttrs);\r\ntabulate(nodeAttrs)\r\n<\/pre><pre class=\"codeoutput\">    Value    Count   Percent\r\n   Mr. Hi       17     50.00%\r\n  Officer       17     50.00%\r\n<\/pre><p>Let's extract the nodes that belong to Mr. Hi's faction.<\/p><pre class=\"codeinput\">group_hi = 1:length(nodeAttrs);\r\ngroup_hi = group_hi(nodeAttrs == <span class=\"string\">'Mr. Hi'<\/span>);\r\n<\/pre><h4>Visualizing the Graph in MATLAB<a name=\"5f223f12-7569-4a34-8596-de7ae6d4640c\"><\/a><\/h4><p>MATLAB also provides <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/graph-and-network-algorithms.html\">graph and network capabilities<\/a> and we can use them to visualize the graph.<\/p><p>Let's convert Python <tt>int<\/tt> values in the edge list to <tt>double<\/tt> and extract the nodes in the edges into separate vectors.<\/p><pre class=\"codeinput\">s = cellfun(@(x) double(x{1}), edgeC);\r\nt = cellfun(@(x) double(x{2}), edgeC);\r\n<\/pre><p>MATLAB <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/graph.html\"><tt>graph<\/tt><\/a> expects column vectors of nodes. Let's transpose them.<\/p><pre class=\"codeinput\">s = s';\r\nt = t';\r\n<\/pre><p>The node indices in Python starts with 0, but the node indices must start with non-zero value in MATLAB. Let's fix this issue.<\/p><pre class=\"codeinput\">s = s + 1;\r\nt = t + 1;\r\n<\/pre><p>Now, we are ready to create a MATLAB graph object and plot it, with Mr. Hi's faction highlighted.<\/p><pre class=\"codeinput\">G = graph(s,t);\r\nG.Nodes.club = nodeAttrs';\r\nfigure\r\nP1 = plot(G);\r\nhighlight(P1, group_hi,<span class=\"string\">'NodeColor'<\/span>, <span class=\"string\">'#D95319'<\/span>, <span class=\"string\">'EdgeColor'<\/span>, <span class=\"string\">'#D95319'<\/span>)\r\ntitle({<span class=\"string\">'Zachary''s Karate Club'<\/span>,<span class=\"string\">'Orange represents Mr. Hi''s faction'<\/span>})\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/2020\/matlab_speaks_python_loren_01.png\" alt=\"\"> <h4>Passing Data from MATLAB to Python<a name=\"2b573019-e33f-440c-957a-78fb5979636b\"><\/a><\/h4><p>In this case, we already have the NetworkX graph object, but for the sake of completeness, let's see how we could create this Python object within MATLAB.<\/p><p>Let's create an empty NetworkX graph.<\/p><pre class=\"codeinput\">nxG2 = py.networkx.Graph();\r\n<\/pre><p>You can add edges to this graph with the <tt>add_edges_from<\/tt> method. It accepts a Python <tt>list<\/tt> of <tt>tuple<\/tt> elements like this:<\/p><pre class=\"language-matlab\">[(1,2),(2,3),(3,4)]\r\n<\/pre><p>This is not a valid syntax in MATLAB. Instead we can use a 1xN <tt>cell<\/tt> array of node pairs like this:<\/p><pre class=\"codeinput\">myListofTuples = {{1,2},{2,3},{3,4}};\r\n<\/pre><p>When we pass this nested <tt>cell<\/tt> array to <tt>py.list<\/tt>, MATLAB automatically converts it to a Python <tt>list<\/tt> of <tt>tuple<\/tt> elements.<\/p><pre class=\"codeinput\">myListofTuples = py.list(myListofTuples);\r\nmyDataType = class(myListofTuples{1})\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'py.tuple'\r\n<\/pre><p>Let's extract the edge list from the MATLAB <tt>graph<\/tt>. It is a 78x2 matrix of <tt>double<\/tt> values. In MATLAB, <tt>double<\/tt> is the default numeric data type.<\/p><pre class=\"codeinput\">edgeL = G.Edges.EndNodes;\r\nmyDataType = class(edgeL)\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'double'\r\n<\/pre><p>If we convert an array of <tt>double<\/tt> values to a Python <tt>list<\/tt>, the values will be converted to Python <tt>float<\/tt>, but the default numeric data type in Python is <tt>int<\/tt>. So we cannot use <tt>double<\/tt>.<\/p><pre class=\"codeinput\">listContent = py.list(edgeL(1,:))\r\n<\/pre><pre class=\"codeoutput\">listContent = \r\n  Python list with no properties.\r\n\r\n    [1.0, 2.0]\r\n<\/pre><p>Also, Python indexing is 0-based while MATLAB is 1-based. We need to convert the array of <tt>double<\/tt> elements to <tt>int8<\/tt> and change the variable elements to 0-based indexing.<\/p><pre class=\"codeinput\">edgeL = int8(edgeL) - 1;\r\nmyDataType = class(edgeL)\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'int8'\r\n<\/pre><p>We can use <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/num2cell.html\"><tt>num2cell<\/tt><\/a> to convert the matrix of <tt>int8<\/tt> values to a 78x2 <tt>cell<\/tt> array, where each element is in a separate cell.<\/p><pre class=\"codeinput\">edgeL = num2cell(edgeL);\r\nmyDataType = class(edgeL)\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'cell'\r\n<\/pre><p>We can place the node pairs in the same cell by converting the 78x2 <tt>cell<\/tt> array to a 78x1 <tt>cell<\/tt> array using <tt>num2cell<\/tt>.<\/p><pre class=\"codeinput\">edgeL = num2cell(edgeL,2);\r\n[rows,cols] = size(edgeL)\r\n<\/pre><pre class=\"codeoutput\">rows =\r\n    78\r\ncols =\r\n     1\r\n<\/pre><p>The <tt>add_edges_from<\/tt> method expects a 1xN Python <tt>list<\/tt>. Now let's turn this into a 1xN <tt>cell<\/tt> array by transposing the Nx1 <tt>cell<\/tt> array, converting it to a Python <tt>list<\/tt> and adding it to the empty NetworkX graph object.<\/p><pre class=\"codeinput\">nxG2.add_edges_from(py.list(edgeL'));\r\n<\/pre><p>The edges were added to the NetworkX graph object. Let's check the first 5 <tt>tuple<\/tt> values.<\/p><pre class=\"codeinput\">edgeL = py.list(nxG2.edges);\r\nlistContent = edgeL(1:5)\r\n<\/pre><pre class=\"codeoutput\">listContent = \r\n  Python list with no properties.\r\n\r\n    [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]\r\n<\/pre><p>The nodes were also added in the graph, but they currently don't have any attributes, as you can see below in the first 3 elements of the node list.<\/p><pre class=\"codeinput\">nodeL = py.list(nxG2.nodes.data);\r\nlistContent = nodeL(1:3)\r\n<\/pre><pre class=\"codeoutput\">listContent = \r\n  Python list with no properties.\r\n\r\n    [(0, {}), (1, {}), (2, {})]\r\n<\/pre><p>To add attributes, we need to use the <tt>set_node_attributes<\/tt> method. This method expects a nested Python <tt>dict<\/tt>. Here is how to create a <tt>dict<\/tt> in MATLAB.<\/p><pre class=\"codeinput\">myDict = py.dict(pyargs(<span class=\"string\">'key'<\/span>, <span class=\"string\">'value'<\/span>))\r\n<\/pre><pre class=\"codeoutput\">myDict = \r\n  Python dict with no properties.\r\n\r\n    {'key': 'value'}\r\n<\/pre><p>The <tt>set_node_attributes<\/tt> method expects a nested <tt>dict<\/tt>. The keys of the outer <tt>dict<\/tt> are the nodes, and values are <tt>dict<\/tt> <tt>arrays<\/tt> of key-value pairs like this:<\/p><pre class=\"language-matlab\">{0: {<span class=\"string\">'club'<\/span>: <span class=\"string\">'Mr. Hi'<\/span>}, 1: {<span class=\"string\">'club'<\/span>: <span class=\"string\">'Officer'<\/span>}}\r\n<\/pre><p>Unfortunately, this won't work, because <tt>pyargs<\/tt> expects only a <tt>string<\/tt> or <tt>char<\/tt> value as the key.<\/p><pre class=\"language-matlab\">&gt;&gt; py.dict(pyargs(0, py.dict(pyargs(<span class=\"string\">'club'<\/span>, <span class=\"string\">'Mr. Hi'<\/span>)))) Error <span class=\"string\">using<\/span>\r\npyargs <span class=\"string\">Field<\/span> <span class=\"string\">names<\/span> <span class=\"string\">must<\/span> <span class=\"string\">be<\/span> <span class=\"string\">string<\/span> <span class=\"string\">scalars<\/span> <span class=\"string\">or<\/span> <span class=\"string\">character<\/span> <span class=\"string\">vectors.<\/span>\r\n<\/pre><p>Instead, we can create an empty <tt>dict<\/tt>, and add the inner <tt>dict<\/tt> from the <tt>tuple<\/tt> data, using 0-based indexing, with the <tt>update<\/tt> method like this:<\/p><pre class=\"codeinput\">attrsD = py.dict;\r\n<span class=\"keyword\">for<\/span> ii = 1:length(nodeAttrs)\r\n    attrD = py.dict(pyargs(<span class=\"string\">'club'<\/span>, G.Nodes.club(ii)));\r\n    attrsD.update(py.tuple({{int8(ii - 1), attrD}}))\r\n<span class=\"keyword\">end<\/span>\r\n<\/pre><p>Then we can use the <tt>set_node_attributes<\/tt> to add attributes to the nodes.<\/p><pre class=\"codeinput\">py.networkx.set_node_attributes(nxG2, attrsD);\r\nnodeL = py.list(nxG2.nodes.data);\r\nlistContent = nodeL(1:3)\r\n<\/pre><pre class=\"codeoutput\">listContent = \r\n  Python list with no properties.\r\n\r\n    [(0, {'club': 'Mr. Hi'}), (1, {'club': 'Mr. Hi'}), (2, {'club': 'Mr. Hi'})]\r\n<\/pre><h4>Community Detection with NetworkX<a name=\"8d14c1a3-42b0-44d7-9d49-07e95a8a1e78\"><\/a><\/h4><p>NetworkX provides the <tt>greedy_modularity_communities<\/tt> method to find communities within a graph. Let's try this algorithm to see how well it can detect the factions!<\/p><p>Since this club split into two groups, we expect to see 2 communities.<\/p><pre class=\"codeinput\">communitiesL = py.networkx.algorithms.community.greedy_modularity_communities(nxG2);\r\nmyDataType = class(communitiesL)\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'py.list'\r\n<\/pre><p>The returned Python <tt>list<\/tt> contains 3 elements. That means the algorithm detected 3 communities within this graph.<\/p><pre class=\"codeinput\">num_communitieis = length(communitiesL)\r\n<\/pre><pre class=\"codeoutput\">num_communitieis =\r\n     3\r\n<\/pre><p>The <tt>list<\/tt> contains a <tt>frozenset<\/tt>. A Python <tt>frozenset<\/tt> is the same as a Python <tt>set<\/tt>, except its elements are immutable. And a Python <tt>set<\/tt> is similar to a Python <tt>list<\/tt>, except all its elements are unique, whereas a <tt>list<\/tt> can contain the same element multiple times.<\/p><pre class=\"codeinput\">listContent = communitiesL{1}\r\n<\/pre><pre class=\"codeoutput\">listContent = \r\n  Python frozenset with no properties.\r\n\r\n    frozenset({32, 33, 8, 14, 15, 18, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31})\r\n<\/pre><p>Let's convert it into nested <tt>cells<\/tt>.<\/p><pre class=\"codeinput\">communitiesC = cell(communitiesL);\r\ncommunitiesC = cellfun(@(x) cell(py.list(x)), communitiesC, <span class=\"string\">'UniformOutput'<\/span>, false);\r\nmyDataType = class(communitiesC{1}{1})\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'py.int'\r\n<\/pre><p>The inner most <tt>cell<\/tt> contain Python <tt>int<\/tt> values. Let's convert them to <tt>double<\/tt>.<\/p><pre class=\"codeinput\"><span class=\"keyword\">for<\/span> ii = 1:length(communitiesC)\r\n    communitiesC{ii} = cellfun(@double, communitiesC{ii});\r\n<span class=\"keyword\">end<\/span>\r\nmyDataType = class(communitiesC{1}(1))\r\n<\/pre><pre class=\"codeoutput\">myDataType =\r\n    'double'\r\n<\/pre><p>Since the nodes are 0-based indexed in Python, we need to change them to 1-based indexed in MATLAB.<\/p><pre class=\"codeinput\">communitiesC = cellfun(@(x) x + 1, communitiesC, <span class=\"string\">'UniformOutput'<\/span>, false);\r\n<\/pre><p>Let's plot the communities within the graph.<\/p><pre class=\"codeinput\">tiledlayout(1,2)\r\nnexttile\r\nP1 = plot(G);\r\nhighlight(P1, group_hi,<span class=\"string\">'NodeColor'<\/span>, <span class=\"string\">'#D95319'<\/span>, <span class=\"string\">'EdgeColor'<\/span>, <span class=\"string\">'#D95319'<\/span>)\r\ntitle({<span class=\"string\">'Zachary''s Karate Club'<\/span>,<span class=\"string\">'Orange represents Mr. Hi''s faction'<\/span>})\r\nnexttile\r\nP2 = plot(G);\r\nhighlight(P2, communitiesC{1},<span class=\"string\">'NodeColor'<\/span>, <span class=\"string\">'#0072BD'<\/span>, <span class=\"string\">'EdgeColor'<\/span>, <span class=\"string\">'#0072BD'<\/span>)\r\nhighlight(P2, communitiesC{2},<span class=\"string\">'NodeColor'<\/span>, <span class=\"string\">'#D95319'<\/span>, <span class=\"string\">'EdgeColor'<\/span>, <span class=\"string\">'#D95319'<\/span>)\r\nhighlight(P2, communitiesC{3},<span class=\"string\">'NodeColor'<\/span>, <span class=\"string\">'#77AC30'<\/span>, <span class=\"string\">'EdgeColor'<\/span>, <span class=\"string\">'#77AC30'<\/span>)\r\ntitle({<span class=\"string\">'Zachary''s Karate Club'<\/span>,<span class=\"string\">'Modularity-based Communities'<\/span>})\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/2020\/matlab_speaks_python_loren_02.png\" alt=\"\"> <p>If you compare these plots, you can see that the two communities on the right in orange and green, when combined, roughly overlap with Mr. Hi's faction.<\/p><p>We can also see that:<\/p><div><ul><li>Community 1 represents the 'Officer' faction<\/li><li>Community 3 represents the devoted 'Mr. Hi' faction<\/li><li>Community 2 represents the people who had connections with both factions<\/li><\/ul><\/div><p>Interestingly, Community 2 ultimately ended up siding with Mr. Hi's faction.<\/p><p>Let's see if there is any difference between the output of the algorithm and the actual faction.<\/p><pre class=\"codeinput\">diff_elements = setdiff(group_hi, [communitiesC{2} communitiesC{3}]);\r\ndiff_elements = [diff_elements setdiff([communitiesC{2} communitiesC{3}], group_hi)]\r\n<\/pre><pre class=\"codeoutput\">diff_elements =\r\n     9    10\r\n<\/pre><p>The community detection algorithm came very close to identifying the actual faction.<\/p><h4>Streamlining the Code<a name=\"9d052ed3-37bb-405d-a70e-52475e7ba6a1\"><\/a><\/h4><p>Up to this point we have been examining what data type is returned in each step. If you already know the data types, you can combine many of these steps into a few lines of code.<\/p><p>To get the karate club data and create a MATLAB graph, you can just do this:<\/p><pre class=\"codeinput\">nxG = py.networkx.karate_club_graph();\r\nedgeC = cellfun(@cell, cell(py.list(nxG.edges)), <span class=\"string\">'UniformOutput'<\/span>, false);\r\nnodeC = cellfun(@cell, cell(py.list(nxG.nodes.data)), <span class=\"string\">'UniformOutput'<\/span>, false);\r\nnodeAttrs = cellfun(@(x) struct(x{2}), nodeC);\r\nnodeAttrs = arrayfun(@(x) string(x.club), nodeAttrs);\r\ns = cellfun(@(x) double(x{1}), edgeC)' + 1;\r\nt = cellfun(@(x) double(x{2}), edgeC)' + 1;\r\nG = graph(s,t);\r\nG.Nodes.club = nodeAttrs';\r\n<\/pre><p>To create a Python graph from the MATLAB data, you can do this:<\/p><pre class=\"codeinput\">nxG2 = py.networkx.Graph();\r\nedgeL = num2cell(int8(G.Edges.EndNodes) - 1);\r\nnxG2.add_edges_from(py.list(num2cell(edgeL, 2)'));\r\nattrsD = py.dict;\r\n<span class=\"keyword\">for<\/span> ii = 1:length(G.Nodes.club)\r\n    attrD = py.dict(pyargs(<span class=\"string\">'club'<\/span>, G.Nodes.club(ii)));\r\n    attrsD.update(py.tuple({{int8(ii - 1), attrD}}))\r\n<span class=\"keyword\">end<\/span>\r\npy.networkx.set_node_attributes(nxG2, attrsD);\r\n<\/pre><p>And to detect the communities, you can do this:<\/p><pre class=\"codeinput\">communitiesC = cell(py.networkx.algorithms.community.greedy_modularity_communities(nxG2));\r\ncommunitiesC = cellfun(@(x) cell(py.list(x)), communitiesC, <span class=\"string\">'UniformOutput'<\/span>, false);\r\n<span class=\"keyword\">for<\/span> ii = 1:length(communitiesC)\r\n    communitiesC{ii} = cellfun(@double, communitiesC{ii});\r\n<span class=\"keyword\">end<\/span>\r\ncommunitiesC = cellfun(@(x) x + 1, communitiesC, <span class=\"string\">'UniformOutput'<\/span>, false);\r\n<\/pre><h4>Summary<a name=\"e092f08e-14d1-4d4a-86ea-33a416f76e88\"><\/a><\/h4><p>In this example, we saw how we can use Python within MATLAB. It is fairly straight forward once you understand how the data type conversion works. Things to remember:<\/p><div><ul><li>Python is 0-based indexed vs MATLAB is 1-based indexed<\/li><li>Python's default numeric data type is <tt>int<\/tt> whereas it's <tt>double<\/tt> for MATLAB<\/li><li>Instead of loops, convert Python data into suitable types of MATLAB arrays<\/li><li>Use <tt>cell<\/tt> arrays for Python <tt>list<\/tt> and <tt>tuple<\/tt><\/li><li>Use <tt>struct<\/tt> arrays for Python <tt>dict<\/tt><\/li><\/ul><\/div><p>In this example, we used a Python library in our MATLAB workflow to get the data and detect communities. I could have coded everything in MATLAB, but it was easier to leverage existing Python code and I was able to complete my tasks within the familiar MATLAB environment where I can be most productive.<\/p><p>Are you a coding polyglot? Share how you use MATLAB and Python together <a href=\"https:\/\/blogs.mathworks.com\/loren\/?p=3567#respond\">here<\/a>.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_eeac4796efca4708b5486ecc7ba160fc() {\r\n        \/\/ Remember the title so we can use it in the new page\r\n        title = document.title;\r\n\r\n        \/\/ Break up these strings so that their presence\r\n        \/\/ in the Javascript doesn't mess up the search for\r\n        \/\/ the MATLAB code.\r\n        t1='eeac4796efca4708b5486ecc7ba160fc ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' eeac4796efca4708b5486ecc7ba160fc';\r\n    \r\n        b=document.getElementsByTagName('body')[0];\r\n        i1=b.innerHTML.indexOf(t1)+t1.length;\r\n        i2=b.innerHTML.indexOf(t2);\r\n \r\n        code_string = b.innerHTML.substring(i1, i2);\r\n        code_string = code_string.replace(\/REPLACE_WITH_DASH_DASH\/g,'--');\r\n\r\n        \/\/ Use \/x3C\/g instead of the less-than character to avoid errors \r\n        \/\/ in the XML parser.\r\n        \/\/ Use '\\x26#60;' instead of '<' so that the XML parser\r\n        \/\/ doesn't go ahead and substitute the less-than character. \r\n        code_string = code_string.replace(\/\\x3C\/g, '\\x26#60;');\r\n\r\n        copyright = 'Copyright 2020 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('<pre>\\n');\r\n        d.write(code_string);\r\n\r\n        \/\/ Add copyright line at the bottom if specified.\r\n        if (copyright.length > 0) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (copyright.length > 0) {\r\n                d.writeln('% _' + copyright + '_');\r\n            }\r\n        }\r\n\r\n        d.write('<\/pre>\\n');\r\n\r\n        d.title = title + ' (MATLAB code)';\r\n        d.close();\r\n    }   \r\n     --> <\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_eeac4796efca4708b5486ecc7ba160fc()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n      the MATLAB code <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; R2019b<br><\/p><\/div><!--\r\neeac4796efca4708b5486ecc7ba160fc ##### SOURCE BEGIN #####\r\n%% MATLAB Speaks Python\r\n% MATLAB is a great computing environment for engineers and scientists.\r\n% MATLAB also provides access to general-purpose languages including C\/C++,\r\n% Java, Fortran, .NET, and Python. Today's guest blogger,\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/profile\/authors\/951521 Toshi\r\n% Takeuchi>, would like to talk about\r\n% <https:\/\/www.mathworks.com\/products\/matlab\/matlab-and-python.html using\r\n% MATLAB with Python>.\r\n%\r\n%% Why Not Use Both? \r\n% When we discuss languages, we often encounter a\r\n% <https:\/\/en.wikipedia.org\/wiki\/False_dilemma false choice> where you feel\r\n% you must choose one or the other. In reality, you can often use both.\r\n% Most of us don't work alone. As part of a larger team, your work is often\r\n% part of a larger workflow that involves multiple languages. That's why\r\n% MATLAB provides interoperability with other languages including Python.\r\n% Your colleagues may want to take advantage of your MATLAB code, or you\r\n% need to access Python-based functionality from your IT systems. MATLAB\r\n% supports your workflow in both directions.\r\n% \r\n% Today I would like to focus on *calling Python from MATLAB* to take\r\n% advantage of some existing Python functionality within a MATLAB-based\r\n% workflow.\r\n% \r\n% In this post, we will see:\r\n%\r\n% * How to import data from Python into MATLAB\r\n% * How to pass data from MATLAB to Python\r\n% * How to use a Python package in MATLAB\r\n%\r\n%% Setting up Python in MATLAB\r\n% MATLAB supports\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/matlab_external\/system-and-configuration-requirements.html\r\n% Python 2.7, 3.6 and 3.7> as of this writing (R2019b).\r\n% \r\n% I assume you already know how to install and manage Python environments\r\n% and dependencies on your platform of choice, and I will not discuss it\r\n% here because it is a complicated topic of its own.\r\n% \r\n% Let's enable access to Python in MATLAB. You need to find the full path\r\n% to your Python executable. Here is an example for Windows. On Mac and\r\n% Linux, your operating system command may be different.\r\n\r\npe = pyenv;\r\nif pe.Status == \"NotLoaded\"\r\n    [~,exepath] = system(\"where python\");\r\n    pe = pyenv('Version',exepath);\r\nend\r\n%% \r\n% If that doesn't work, you can also just pass the path to your Python\r\n% executable as string.\r\n% \r\n%   pe =\r\n%   pyenv('Version','C:\\Users\\username\\AppData\\Local\\your\\python\\path\\python.exe')\r\n%\r\n\r\nmyPythonVersion = pe.Version\r\npy.print(\"Hello, Python!\")\r\n\r\n%% Karate Club Dataset\r\n% <http:\/\/www1.ind.ku.dk\/complexLearning\/zachary1977.pdf Wayne Zachary>\r\n% published a\r\n% <http:\/\/vlado.fmf.uni-lj.si\/pub\/networks\/data\/Ucinet\/UciData.htm#zachary\r\n% dataset> that contains a social network of friendships between 34 members\r\n% of a karate club at a US university in the 1970s. A dispute that erupted\r\n% in this club eventually caused it to break up into two factions. We want\r\n% to see if we can algorithmically predict how the club would break up\r\n% based on its interpersonal relationships.\r\n% \r\n% This dataset is included in <https:\/\/networkx.github.io\/ NetworkX>, a\r\n% complex networks package for Python. We can easily get started by\r\n% importing the dataset using this package.\r\n% \r\n% I am using NetworkX 2.2. To check the package version in Python, you\r\n% would typically use the version package attribute like this:\r\n% \r\n%   >>> networkx.__version__\r\n%\r\n% MATLAB doesn't support\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/matlab_external\/limitations-to-python-support.html\r\n% class names or other identifiers starting with an underscore (_)\r\n% character>. Instead, use the following to get the help content on the\r\n% package, including its current version.\r\n% \r\n%   > py.help('networkx')\r\n%\r\n%% To Import or Not to Import\r\n% Typically, you do this at the start of your Python script.\r\n% \r\n%   import networkx as nx \r\n%   G = nx.karate_club_graph()\r\n%\r\n% However, this is not recommended in MATLAB because the behavior of the\r\n% |import| function in MATLAB is different from Python's.\r\n% \r\n% The MATLAB way to call Python is to use |*py*|, followed by a package or\r\n% method like this:\r\n\r\nnxG = py.networkx.karate_club_graph();\r\n\r\n%% \r\n% If you must use |import|, you can do it as follows:\r\n% \r\n%   import py.networkx.* \r\n%   nxG = karate_club_graph();\r\n%\r\n% As you can see, it is hard to remember that we are calling a Python\r\n% method when you omit |py|, which can be confusing when you start mixing\r\n% MATLAB code and Python code within the same script.\r\n%\r\n%% Extracting Data from a Python Object\r\n% The following returns the karate club dataset in a NetworkX graph object.\r\n\r\nmyDataType = class(nxG)\r\n\r\n%% \r\n% You can see the methods available on this object like this:\r\n% \r\n%   methods(nxG)\r\n%\r\n% You can also see the properties of this object.\r\n% \r\n%   properties(nxG)\r\n%\r\n% A NetworkX graph contains an |edges| property that returns an object\r\n% called |EdgeView|.\r\n\r\nedgeL = nxG.edges;\r\nmyDataType = class(edgeL)\r\n\r\n%% \r\n% To use this Python object in MATLAB, the first step is to convert the\r\n% object into a core Python data type such as a Python |list|.\r\n\r\nedgeL = py.list(edgeL);\r\nmyDataType = class(edgeL)\r\n\r\n%% \r\n% Now |edgeL| contains a Python |list| of node pairs stored as Python\r\n% |tuple| elements. Each node pair represents an edge in the graph. Let's\r\n% see the first 5 |tuple| values.\r\n\r\nlistContent = edgeL(1:5)\r\n\r\n%% Handling a Python List and Tuple\r\n% The Python way for handling a |list| or |tuple| typically looks like\r\n% this, where you process individual elements in a loop.\r\n% \r\n%   for i in l: print i             # l is the list \r\n%   for u, v in t: print((u, v))    # t is the tuple\r\n%\r\n% The MATLAB way is to use arrays instead. The Python |list| can be\r\n% converted into a |cell| array.\r\n\r\nedgeC = cell(edgeL);\r\nmyDataType = class(edgeC)\r\n\r\n%% \r\n% This |cell| array contains Python |tuple| elements.\r\n\r\nmyDataType = class(edgeC{1})\r\n\r\n%% \r\n% The Python |tuple| can also be converted to a |cell| array. To convert\r\n% the inner |tuple| elements, we can use\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/cellfun.html |cellfun|>.\r\n\r\nedgeC = cellfun(@cell, edgeC, 'UniformOutput', false);\r\nmyDataType = class(edgeC{1})\r\n\r\n%% \r\n% The resulting nested |cell| array contains Python |int| values.\r\n\r\nmyDataType = class(edgeC{1}{1})\r\n\r\n%% Handling a Python Dict\r\n% Now let's also extract the nodes from the dataset. We can follow the same\r\n% steps as we did for the edges.\r\n\r\nnodeL = py.list(nxG.nodes.data);\r\nnodeC = cell(nodeL);\r\nnodeC = cellfun(@cell, nodeC, 'UniformOutput', false);\r\n\r\n%% \r\n% An inner |cell| array contains both Python |int| and |dict| elements.\r\n\r\ncellContent = nodeC{1}\r\n\r\n%% \r\n% Python |dict| is a data type based on key-value pairs. In this case, the\r\n% key is |'club'| and the value is |'Mr. Hi'|.\r\n\r\ncellContent = nodeC{1}{2}\r\n\r\n%% \r\n% Mr. Hi was a karate instructor at the club. The other value in the Python\r\n% |dict| is |'Officer'|, and the officer was a leader of the club. They\r\n% were the key individuals of the respective factions. The node attribute\r\n% indicates which faction an individual node belongs to. In this case, Node\r\n% 1 belonged to Mr. Hi's faction.\r\n% \r\n% The Python way for handling a |dict| typically looks like this, where you\r\n% process individual elements in a loop.\r\n% \r\n%   for k, v in d.items():\r\n%       print (k, v)\r\n%\r\n% Again, the MATLAB way is to use an array. The Python |dict| can be\r\n% converted to a |struct| array.\r\n\r\nnodeAttrs = cellfun(@(x) struct(x{2}), nodeC);\r\nmyDataType = class(nodeAttrs)\r\n\r\n%% \r\n% We can extract the individual values into a |string| array. The club was\r\n% evidently evenly divided between the factions.\r\n\r\nnodeAttrs = arrayfun(@(x) string(x.club), nodeAttrs);\r\ntabulate(nodeAttrs)\r\n\r\n%% \r\n% Let's extract the nodes that belong to Mr. Hi's faction.\r\n\r\ngroup_hi = 1:length(nodeAttrs);\r\ngroup_hi = group_hi(nodeAttrs == 'Mr. Hi');\r\n\r\n%% Visualizing the Graph in MATLAB\r\n% MATLAB also provides\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/graph-and-network-algorithms.html\r\n% graph and network capabilities> and we can use them to visualize the\r\n% graph.\r\n% \r\n% Let's convert Python |int| values in the edge list to |double| and\r\n% extract the nodes in the edges into separate vectors.\r\n\r\ns = cellfun(@(x) double(x{1}), edgeC);\r\nt = cellfun(@(x) double(x{2}), edgeC);\r\n%% \r\n% MATLAB <https:\/\/www.mathworks.com\/help\/matlab\/ref\/graph.html |graph|>\r\n% expects column vectors of nodes. Let's transpose them.\r\n\r\ns = s';\r\nt = t';\r\n\r\n%% \r\n% The node indices in Python starts with 0, but the node indices must start\r\n% with non-zero value in MATLAB. Let's fix this issue.\r\n\r\ns = s + 1;\r\nt = t + 1;\r\n\r\n%% \r\n% Now, we are ready to create a MATLAB graph object and plot it, with Mr.\r\n% Hi's faction highlighted.\r\n\r\nG = graph(s,t);\r\nG.Nodes.club = nodeAttrs';\r\nfigure\r\nP1 = plot(G);\r\nhighlight(P1, group_hi,'NodeColor', '#D95319', 'EdgeColor', '#D95319')\r\ntitle({'Zachary''s Karate Club','Orange represents Mr. Hi''s faction'})\r\n\r\n%% Passing Data from MATLAB to Python\r\n% In this case, we already have the NetworkX graph object, but for the sake\r\n% of completeness, let's see how we could create this Python object within\r\n% MATLAB.\r\n% \r\n% Let's create an empty NetworkX graph.\r\n\r\nnxG2 = py.networkx.Graph();\r\n\r\n%% \r\n% You can add edges to this graph with the |add_edges_from| method. It\r\n% accepts a Python |list| of |tuple| elements like this:\r\n% \r\n%   [(1,2),(2,3),(3,4)]\r\n%\r\n% This is not a valid syntax in MATLAB. Instead we can use a 1xN |cell|\r\n% array of node pairs like this:\r\n\r\nmyListofTuples = {{1,2},{2,3},{3,4}};\r\n\r\n%% \r\n% When we pass this nested |cell| array to |py.list|, MATLAB automatically\r\n% converts it to a Python |list| of |tuple| elements.\r\n\r\nmyListofTuples = py.list(myListofTuples);\r\nmyDataType = class(myListofTuples{1})\r\n\r\n%% \r\n% Let's extract the edge list from the MATLAB |graph|. It is a 78x2 matrix\r\n% of |double| values. In MATLAB, |double| is the default numeric data type.\r\n\r\nedgeL = G.Edges.EndNodes;\r\nmyDataType = class(edgeL)\r\n\r\n%% \r\n% If we convert an array of |double| values to a Python |list|, the values\r\n% will be converted to Python |float|, but the default numeric data type in\r\n% Python is |int|. So we cannot use |double|.\r\n\r\nlistContent = py.list(edgeL(1,:))\r\n\r\n%% \r\n% Also, Python indexing is 0-based while MATLAB is 1-based. We need to\r\n% convert the array of |double| elements to |int8| and change the variable\r\n% elements to 0-based indexing.\r\n\r\nedgeL = int8(edgeL) - 1;\r\nmyDataType = class(edgeL)\r\n\r\n%% \r\n% We can use <https:\/\/www.mathworks.com\/help\/matlab\/ref\/num2cell.html\r\n% |num2cell|> to convert the matrix of |int8| values to a 78x2 |cell|\r\n% array, where each element is in a separate cell.\r\n\r\nedgeL = num2cell(edgeL);\r\nmyDataType = class(edgeL)\r\n\r\n%% \r\n% We can place the node pairs in the same cell by converting the 78x2\r\n% |cell| array to a 78x1 |cell| array using |num2cell|.\r\n\r\nedgeL = num2cell(edgeL,2);\r\n[rows,cols] = size(edgeL)\r\n\r\n%% \r\n% The |add_edges_from| method expects a 1xN Python |list|. Now let's turn\r\n% this into a 1xN |cell| array by transposing the Nx1 |cell| array,\r\n% converting it to a Python |list| and adding it to the empty NetworkX\r\n% graph object.\r\n\r\nnxG2.add_edges_from(py.list(edgeL'));\r\n\r\n%% \r\n% The edges were added to the NetworkX graph object. Let's check the first\r\n% 5 |tuple| values.\r\n\r\nedgeL = py.list(nxG2.edges);\r\nlistContent = edgeL(1:5)\r\n\r\n%% \r\n% The nodes were also added in the graph, but they currently don't have any\r\n% attributes, as you can see below in the first 3 elements of the node\r\n% list.\r\n\r\nnodeL = py.list(nxG2.nodes.data);\r\nlistContent = nodeL(1:3)\r\n\r\n%% \r\n% To add attributes, we need to use the |set_node_attributes| method. This\r\n% method expects a nested Python |dict|. Here is how to create a |dict| in\r\n% MATLAB.\r\n\r\nmyDict = py.dict(pyargs('key', 'value'))\r\n\r\n%% \r\n% The |set_node_attributes| method expects a nested |dict|. The keys of the\r\n% outer |dict| are the nodes, and values are |dict| |arrays| of key-value\r\n% pairs like this:\r\n% \r\n%   {0: {'club': 'Mr. Hi'}, 1: {'club': 'Officer'}}\r\n%\r\n% Unfortunately, this won't work, because |pyargs| expects only a |string|\r\n% or |char| value as the key.\r\n% \r\n%   >> py.dict(pyargs(0, py.dict(pyargs('club', 'Mr. Hi')))) Error using\r\n%   pyargs Field names must be string scalars or character vectors.\r\n%\r\n% Instead, we can create an empty |dict|, and add the inner |dict| from the\r\n% |tuple| data, using 0-based indexing, with the |update| method like this:\r\n\r\nattrsD = py.dict;\r\nfor ii = 1:length(nodeAttrs)\r\n    attrD = py.dict(pyargs('club', G.Nodes.club(ii)));\r\n    attrsD.update(py.tuple({{int8(ii - 1), attrD}}))\r\nend\r\n\r\n%%\r\n% Then we can use the |set_node_attributes| to add attributes to the nodes.\r\n\r\npy.networkx.set_node_attributes(nxG2, attrsD);\r\nnodeL = py.list(nxG2.nodes.data);\r\nlistContent = nodeL(1:3)\r\n\r\n%% Community Detection with NetworkX\r\n% NetworkX provides the |greedy_modularity_communities| method to find\r\n% communities within a graph. Let's try this algorithm to see how well it\r\n% can detect the factions!\r\n% \r\n% Since this club split into two groups, we expect to see 2 communities.\r\n\r\ncommunitiesL = py.networkx.algorithms.community.greedy_modularity_communities(nxG2);\r\nmyDataType = class(communitiesL)\r\n\r\n%% \r\n% The returned Python |list| contains 3 elements. That means the algorithm\r\n% detected 3 communities within this graph.\r\n\r\nnum_communitieis = length(communitiesL)\r\n\r\n%% \r\n% The |list| contains a |frozenset|. A Python |frozenset| is the same as a\r\n% Python |set|, except its elements are immutable. And a Python |set| is\r\n% similar to a Python |list|, except all its elements are unique, whereas a\r\n% |list| can contain the same element multiple times.\r\n\r\nlistContent = communitiesL{1}\r\n\r\n%% \r\n% Let's convert it into nested |cells|.\r\n\r\ncommunitiesC = cell(communitiesL);\r\ncommunitiesC = cellfun(@(x) cell(py.list(x)), communitiesC, 'UniformOutput', false);\r\nmyDataType = class(communitiesC{1}{1})\r\n\r\n%% \r\n% The inner most |cell| contain Python |int| values. Let's convert them to\r\n% |double|.\r\n\r\nfor ii = 1:length(communitiesC)\r\n    communitiesC{ii} = cellfun(@double, communitiesC{ii});\r\nend\r\nmyDataType = class(communitiesC{1}(1))\r\n\r\n%% \r\n% Since the nodes are 0-based indexed in Python, we need to change them to\r\n% 1-based indexed in MATLAB.\r\n\r\ncommunitiesC = cellfun(@(x) x + 1, communitiesC, 'UniformOutput', false);\r\n\r\n%% \r\n% Let's plot the communities within the graph.\r\n\r\ntiledlayout(1,2)\r\nnexttile\r\nP1 = plot(G);\r\nhighlight(P1, group_hi,'NodeColor', '#D95319', 'EdgeColor', '#D95319')\r\ntitle({'Zachary''s Karate Club','Orange represents Mr. Hi''s faction'})\r\nnexttile\r\nP2 = plot(G);\r\nhighlight(P2, communitiesC{1},'NodeColor', '#0072BD', 'EdgeColor', '#0072BD')\r\nhighlight(P2, communitiesC{2},'NodeColor', '#D95319', 'EdgeColor', '#D95319')\r\nhighlight(P2, communitiesC{3},'NodeColor', '#77AC30', 'EdgeColor', '#77AC30')\r\ntitle({'Zachary''s Karate Club','Modularity-based Communities'})\r\n\r\n%% \r\n% If you compare these plots, you can see that the two communities on\r\n% the right in orange and green, when combined, roughly overlap with Mr.\r\n% Hi's faction.\r\n% \r\n% We can also see that:\r\n%\r\n% * Community 1 represents the 'Officer' faction\r\n% * Community 3 represents the devoted 'Mr. Hi' faction\r\n% * Community 2 represents the people who had connections with both\r\n% factions\r\n%\r\n% Interestingly, Community 2 ultimately ended up siding with Mr. Hi's\r\n% faction.\r\n% \r\n% Let's see if there is any difference between the output of the algorithm\r\n% and the actual faction.\r\n\r\ndiff_elements = setdiff(group_hi, [communitiesC{2} communitiesC{3}]);\r\ndiff_elements = [diff_elements setdiff([communitiesC{2} communitiesC{3}], group_hi)]\r\n\r\n%% \r\n% The community detection algorithm came very close to identifying the\r\n% actual faction.\r\n%\r\n%% Streamlining the Code\r\n% Up to this point we have been examining what data type is returned in\r\n% each step. If you already know the data types, you can combine many of\r\n% these steps into a few lines of code.\r\n% \r\n% To get the karate club data and create a MATLAB graph, you can just do\r\n% this:\r\n\r\nnxG = py.networkx.karate_club_graph();\r\nedgeC = cellfun(@cell, cell(py.list(nxG.edges)), 'UniformOutput', false);\r\nnodeC = cellfun(@cell, cell(py.list(nxG.nodes.data)), 'UniformOutput', false);\r\nnodeAttrs = cellfun(@(x) struct(x{2}), nodeC);\r\nnodeAttrs = arrayfun(@(x) string(x.club), nodeAttrs);\r\ns = cellfun(@(x) double(x{1}), edgeC)' + 1;\r\nt = cellfun(@(x) double(x{2}), edgeC)' + 1;\r\nG = graph(s,t);\r\nG.Nodes.club = nodeAttrs';\r\n%% \r\n% To create a Python graph from the MATLAB data, you can do this:\r\n\r\nnxG2 = py.networkx.Graph();\r\nedgeL = num2cell(int8(G.Edges.EndNodes) - 1);\r\nnxG2.add_edges_from(py.list(num2cell(edgeL, 2)'));\r\nattrsD = py.dict;\r\nfor ii = 1:length(G.Nodes.club)\r\n    attrD = py.dict(pyargs('club', G.Nodes.club(ii)));\r\n    attrsD.update(py.tuple({{int8(ii - 1), attrD}}))\r\nend\r\npy.networkx.set_node_attributes(nxG2, attrsD);\r\n%% \r\n% And to detect the communities, you can do this:\r\n\r\ncommunitiesC = cell(py.networkx.algorithms.community.greedy_modularity_communities(nxG2));\r\ncommunitiesC = cellfun(@(x) cell(py.list(x)), communitiesC, 'UniformOutput', false);\r\nfor ii = 1:length(communitiesC)\r\n    communitiesC{ii} = cellfun(@double, communitiesC{ii});\r\nend\r\ncommunitiesC = cellfun(@(x) x + 1, communitiesC, 'UniformOutput', false);\r\n%% Summary\r\n% In this example, we saw how we can use Python within MATLAB. It is fairly\r\n% straight forward once you understand how the data type conversion works.\r\n% Things to remember:\r\n%\r\n% * Python is 0-based indexed vs MATLAB is 1-based indexed\r\n% * Python's default numeric data type is |int| whereas it's |double| for\r\n% MATLAB\r\n% * Instead of loops, convert Python data into suitable types of MATLAB\r\n% arrays\r\n% * Use |cell| arrays for Python |list| and |tuple|\r\n% * Use |struct| arrays for Python |dict|\r\n%\r\n% In this example, we used a Python library in our MATLAB workflow to get\r\n% the data and detect communities.\r\n% I could have coded everything in MATLAB, but it was easier to leverage\r\n% existing Python code and I was able to complete my tasks within the\r\n% familiar MATLAB environment where I can be most productive.\r\n%\r\n% Are you a coding polyglot? Share how you use MATLAB and Python together\r\n% <https:\/\/blogs.mathworks.com\/loren\/?p=3567#respond here>.\r\n##### SOURCE END ##### eeac4796efca4708b5486ecc7ba160fc\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img decoding=\"async\"  class=\"img-responsive\" src=\"https:\/\/blogs.mathworks.com\/images\/loren\/2020\/matlab_speaks_python_loren_02.png\" onError=\"this.style.display ='none';\" \/><\/div><!--introduction--><p>MATLAB is a great computing environment for engineers and scientists. MATLAB also provides access to general-purpose languages including C\/C++, Java, Fortran, .NET, and Python. Today's guest blogger, <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/profile\/authors\/951521\">Toshi Takeuchi<\/a>, would like to talk about <a href=\"https:\/\/www.mathworks.com\/products\/matlab\/matlab-and-python.html\">using MATLAB with Python<\/a>.... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/loren\/2020\/03\/03\/matlab-speaks-python\/\">read more >><\/a><\/p>","protected":false},"author":39,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[66,42,85,61],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts\/3567"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/users\/39"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/comments?post=3567"}],"version-history":[{"count":13,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts\/3567\/revisions"}],"predecessor-version":[{"id":3613,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts\/3567\/revisions\/3613"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/media?parent=3567"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/categories?post=3567"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/tags?post=3567"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}