text: #-*- coding: utf-8 -*-
  """
  Mixing matrices for node attributes and degree.
  """
  import networkx as nx
  from networkx.utils import dict_to_numpy_array
  from networkx.algorithms.assortativity.pairs import node_degree_xy, \
      node_attribute_xy
  __author__ = ' '.join(['Aric Hagberg <aric.hagberg@gmail.com>'])
  __all__ = ['attribute_mixing_matrix',
             'attribute_mixing_dict',
             'degree_mixing_matrix',
             'degree_mixing_dict',
             'numeric_mixing_matrix',
             'mixing_dict']
  
  def attribute_mixing_dict(G,attribute,nodes=None,normalized=False):
      """Return dictionary representation of mixing matrix for attribute.
  
      Parameters
      ----------
      G : graph 
         NetworkX graph object.
  
      attribute : string 
         Node attribute key.
  
      nodes: list or iterable (optional)
          Unse nodes in container to build the dict. The default is all nodes.
  
      normalized : bool (default=False)
         Return counts if False or probabilities if True.
  
      Examples
      --------
      >>> G=nx.Graph()
      >>> G.add_nodes_from([0,1],color='red')
      >>> G.add_nodes_from([2,3],color='blue')
      >>> G.add_edge(1,3)
      >>> d=nx.attribute_mixing_dict(G,'color')
      >>> print(d['red']['blue'])
      1
      >>> print(d['blue']['red']) # d symmetric for undirected graphs
      1
  
      Returns
      -------
      d : dictionary
         Counts or joint probability of occurrence of attribute pairs.
      """
      xy_iter=node_attribute_xy(G,attribute,nodes)
      return mixing_dict(xy_iter,normalized=normalized)
  
  
  def attribute_mixing_matrix(G,attribute,nodes=None,mapping=None,
                              normalized=True):
      """Return mixing matrix for attribute.
  
      Parameters
      ----------
      G : graph 
         NetworkX graph object.
  
      attribute : string 
         Node attribute key.
  
      nodes: list or iterable (optional)
          Use only nodes in container to build the matrix. The default is 
          all nodes.
  
      mapping : dictionary, optional        
         Mapping from node attribute to integer index in matrix.  
         If not specified, an arbitrary ordering will be used. 
      
      normalized : bool (default=False)
         Return counts if False or probabilities if True.
  
      Returns
      -------
      m: numpy array
         Counts or joint probability of occurrence of attribute pairs.
      """
      d=attribute_mixing_dict(G,attribute,nodes)
      a=dict_to_numpy_array(d,mapping=mapping)
      if normalized:
          a=a/a.sum()
      return a
  
  
  def degree_mixing_dict(G, x='out', y='in', weight=None, 
                         nodes=None, normalized=False):
      """Return dictionary representation of mixing matrix for degree.
  
      Parameters
      ----------
      G : graph 
          NetworkX graph object.
  
      x: string ('in','out')
         The degree type for source node (directed graphs only).
  
      y: string ('in','out')
         The degree type for target node (directed graphs only).
  
      weight: string or None, optional (default=None)
         The edge attribute that holds the numerical value used 
         as a weight.  If None, then each edge has weight 1.
         The degree is the sum of the edge weights adjacent to the node.
  
      normalized : bool (default=False)
          Return counts if False or probabilities if True.
  
      Returns
      -------
      d: dictionary
         Counts or joint probability of occurrence of degree pairs.
      """
      xy_iter=node_degree_xy(G, x=x, y=y, nodes=nodes, weight=weight)
      return mixing_dict(xy_iter,normalized=normalized)
  
  
  
  def degree_mixing_matrix(G, x='out', y='in', weight=None, 
                           nodes=None, normalized=True):
      """Return mixing matrix for attribute.
  
      Parameters
      ----------
      G : graph 
         NetworkX graph object.
  
      x: string ('in','out')
         The degree type for source node (directed graphs only).
  
      y: string ('in','out')
         The degree type for target node (directed graphs only).
  
      nodes: list or iterable (optional)
          Build the matrix using only nodes in container. 
          The default is all nodes.
  
      weight: string or None, optional (default=None)
         The edge attribute that holds the numerical value used 
         as a weight.  If None, then each edge has weight 1.
         The degree is the sum of the edge weights adjacent to the node.
  
      normalized : bool (default=False)
         Return counts if False or probabilities if True.
  
      Returns
      -------
      m: numpy array
         Counts, or joint probability, of occurrence of node degree.
      """
      d=degree_mixing_dict(G, x=x, y=y, nodes=nodes, weight=weight)
      s=set(d.keys())
      for k,v in d.items():
          s.update(v.keys())
      m=max(s)            
      mapping=dict(zip(range(m+1),range(m+1)))
      a=dict_to_numpy_array(d,mapping=mapping)
      if normalized:
          a=a/a.sum()
      return a
  
  def numeric_mixing_matrix(G,attribute,nodes=None,normalized=True):
      """Return numeric mixing matrix for attribute.
  
      Parameters
      ----------
      G : graph 
         NetworkX graph object.
  
      attribute : string 
         Node attribute key.
  
      nodes: list or iterable (optional)
          Build the matrix only with nodes in container. The default is all nodes.
      
      normalized : bool (default=False)
         Return counts if False or probabilities if True.
  
      Returns
      -------
      m: numpy array
         Counts, or joint, probability of occurrence of node attribute pairs.
      """
      d=attribute_mixing_dict(G,attribute,nodes)
      s=set(d.keys())
      for k,v in d.items():
          s.update(v.keys())
      m=max(s)            
      mapping=dict(zip(range(m+1),range(m+1)))
      a=dict_to_numpy_array(d,mapping=mapping)
      if normalized:
          a=a/a.sum()
      return a
  
  def mixing_dict(xy,normalized=False):
      """Return a dictionary representation of mixing matrix.
  
      Parameters
      ----------
      xy : list or container of two-tuples
         Pairs of (x,y) items. 
  
      attribute : string 
         Node attribute key 
  
      normalized : bool (default=False)
         Return counts if False or probabilities if True.
  
      Returns
      -------
      d: dictionary
         Counts or Joint probability of occurrence of values in xy.
      """
      d={}
      psum=0.0
      for x,y in xy:
          if x not in d:
              d[x]={}
          if y not in d:
              d[y]={}
          v = d[x].get(y,0)
          d[x][y] = v+1
          psum+=1
  
  
      if normalized:
          for k,jdict in d.items():
              for j in jdict:
                  jdict[j]/=psum
      return d
  
  
  
  # fixture for nose tests
  def setup_module(module):
      from nose import SkipTest
      try:
          import numpy
      except:
          raise SkipTest("NumPy not available")
      try:
          import scipy
      except:
          raise SkipTest("SciPy not available")
  