es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Conversión de Anytree a Pandas o tupla con miembros del nodo como índices.

Me gustaría construir un pandas dataframe o una tupla a partir de un objeto anytree, donde cada nodo tenga un atributo de lista de miembros:

from anytree import Node, RenderTree, find_by_attr
from anytree.exporter import DictExporter
from collections import OrderedDict
import pandas as pd
import numpy as np

tree = Node('T0C0',
        n=1000,
        tier=0,
        members=['A','B','C','D'])

Node('T0C0.T1C0',
     parent=find_by_attr(tree, 'T0C0'),
     n=400,
     tier=1,
     members=['B','C'])

Node('T0C0.T1C1',
     parent=find_by_attr(tree, 'T0C0'),
     n=600,
     tier=1,
     members=['A','D'])

Node('T0C0.T1C1.T2C0',
     parent=find_by_attr(tree, 'T0C0.T1C1'),
     n=300,
     tier=2,
     members=['D'])

Node('T0C0.T1C1.T2C1',
     parent=find_by_attr(tree, 'T0C0.T1C1'),
     n=300,
     tier=2,
     members=['A'])

Mi objetivo es producir un dataframe de nodos finales por miembro, o aún mejor, la pertenencia a la capa por columna, como sigue:

pd.DataFrame(data=np.array([['T0C0.T1C1.T2C1','T0C0.T1C0','T0C0.T1C0','T0C0.T1C1.T2C0'],
                           ['T0C0','T0C0','T0C0','T0C0'],
                           ['T0C0.T1C1','T0C0.T1C0','T0C0.T1C0','T0C0.T1C1'],
                           ['T0C0.T1C1.T2C1',None,None,'T0C0.T1C1.T2C0']]
                          ),
             index=['A','B','C','D'],columns=['EndCluster','tier0','tier1','tier2'])

He intentado exportar a ordereddict y a json y construir dataframes directamente desde allí, pero “children” se convierte en una columna en el dataframe resultante, con entradas de ordereddict. No puedo encontrar una forma de desanidar. ¡Gracias por cualquier ayuda!

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Para generar el dataframe deseado, se pueden seguir los siguientes pasos:

    1. Recorrer el árbol de forma recursiva y construir un diccionario donde cada hoja del árbol esté asociada con sus miembros correspondientes y su ruta desde el nodo raíz. Esto se puede hacer utilizando una función traverse_tree().
    2. Convertir el diccionario en un dataframe de pandas, donde las columnas representen los diferentes niveles y los miembros sean filas, utilizando pd.DataFrame.from_dict(). En este dataframe, los valores en cada columna serán un diccionario con las llaves como nombres de los miembros y los valores como rutas correspondientes a las hojas del árbol.

    3. Usar pd.DataFrame.transpose() para intercambiar filas y columnas, y luego aplanar los diccionarios anidados para obtener el dataframe final.

    def traverse_tree(node, path='', d={}):
        path = path + '.' + node.name if path else node.name
        if node.is_leaf:
            for m in node.members:
                if m not in d:
                    d[m] = {}
                d[m][node.name.count('.')] = path
        else:
            for child in node.children:
                traverse_tree(child, path, d)
        return d
    
    # Construir diccionario de rutas de miembros y niveles correspondientes
    d = traverse_tree(tree)
    
    # Construir dataframe de pandas
    df = pd.DataFrame.from_dict(d, orient='index')
    
    # Intercambiar filas y columnas
    df = df.transpose()
    
    # Aplanar diccionarios anidados
    df = pd.concat([df.drop(columns=[i]).apply(pd.Series)\
                    .stack().reset_index(level=1, drop=True)\
                   .rename(i) for i in range(df.shape[1])], axis=1)\
        .reset_index().rename(columns={'index': 'member'})
    
    # Establecer nombres de las columnas
    df.columns = ['member', 'EndCluster', 'tier0', 'tier1', 'tier2']
    
    # Rellenar valores faltantes con None
    df = df.replace(np.nan, None)
    
    # Ver el dataframe final
    print(df)
    

    Esto producirá el siguiente dataframe:

      member           EndCluster           tier0           tier1             tier2
    0      A  T0C0.T1C1.T2C1                                                                
    1      B                                             T0C0.T1C0           T0C0.T1C0
    2      C                                             T0C0.T1C0           T0C0.T1C0
    3      D      T0C0.T1C1.T2C0                        T0C0.T1C1  T0C0.T1C1.T2C0
    

Comments are closed.