Esta es una adición a Ordenar Pandas DataFrame por columna de números como cadena pero creo que merece su propio alcance.

Puedo aplicar la solución del tema vinculado a un solo marco de datos. Pero una vez que combino varios de estos DF, el orden establecido anteriormente se pierde nuevamente.

Empiezo con dos estructuras de datos que tienen estos números impares, representados como cadenas.

data = [
dict(name = 'test1', index = '1' , status='fail'),
dict(name = 'test3', index = '3', status='pass'),
dict(name = 'test1', index = '11', status='pass'),
dict(name = 'test1', index = '1 2 14 56', status='fail'),
dict(name = 'test1', index = '33' , status='pass'),
dict(name = 'test3', index = '20', status='fail'),
dict(name = 'test1', index = '2' , status='fail'),
dict(name = 'test1', index = '22' , status='fail'),
dict(name = 'test3', index = '5:1:50', status='pass'),]

data1 = [
dict(name = 'test1', index = '1' , status='fail'),
dict(name = 'test3', index = '3', status='fail'),
dict(name = 'test1', index = '11', status='pass'),
dict(name = 'test1', index = '1 2 14 56', status='fail'),
dict(name = 'test1', index = '33' , status='pass'),
dict(name = 'test3', index = '20', status='pass'),
dict(name = 'test1', index = '2' , status='fail'),]

Primero los convierto en marcos de datos individuales.

df = pd.DataFrame(data)
df1 = pd.DataFrame(data1)

Ahora tengo p. Ej. df como:

    name      index status
0  test1          1   fail
1  test3          3   pass
2  test1         11   pass
3  test1  1 2 14 56   fail
4  test1         33   pass
5  test3         20   fail
6  test1          2   fail
7  test1         22   fail
8  test3     5:1:50   pass

A continuación, masajeo los dos DF para crear un índice múltiple ordenado que maneja las "cadenas numéricas" de forma no léxica. (Para obtener más información, consulte el tema vinculado anteriormente)

dfs = dict()
for i,d in enumerate((df, df1)):
    d = (d.assign(
          _tmpIdx=d['index'].str.extract(r'([\d]+)').astype(int))
         .sort_values(['name', '_tmpIdx'])
         .drop('_tmpIdx', axis=1)
         .set_index(['name', 'index'])
        )
     dfs[i] = d

Ahora, p. Ej. df se ve así (tenga en cuenta que la columna de índice está ordenada de forma no léxica, a pesar de que los valores son cadenas):

                status
name  index           
test1 1           fail
      1 2 14 56   fail
      2           fail
      11          pass
      22          fail
      33          pass
test3 3           pass
      5:1:50      pass
      20          fail

Ahora concateno los dos DF

summary = pd.concat(dfs.values(), axis=1, keys=dfs.keys())

Desafortunadamente, esto restablece la clasificación anterior a léxica :

                     0      1
                status status
name  index                  
test1 1           fail   fail
      1 2 14 56   fail   fail
      11          pass   pass
      2           fail   fail
      22          fail    NaN
      33          pass   pass
test3 20          fail   pass
      3           pass   fail
      5:1:50      pass    NaN

¿Cómo mantener la clasificación para el nivel interno del índice general? ¿Existe tal vez una mejor manera de lograr esto?

0
twil 4 dic. 2020 a las 13:51

3 respuestas

La mejor respuesta

Yo mismo encontré una solución, que quiero compartir.

En lugar de aplicar la clasificación personalizada a cada DataFrame individual antes de concatenarlos, prefiero hacerlo en el resultado de la concatenación.

Como se indicó anteriormente, primero configuré un índice múltiple para cada uno de mis DF sin procesar (el mismo que en la pregunta). Esto es necesario para que estas columnas no se repitan en el resultado de la concatenación.

dfs = dict()
for i,d in enumerate((df, df1)):
    d = d.sort_values(['name','index']).set_index(['name','index'])
    dfs[i] = d

El resultado todavía se ordenará index léxicamente esta vez. df, p. Ej. ahora se ve así

                status
name  index           
test1 1           fail
      1 2 14 56   fail
      11          pass
      2           fail
      22          fail
      33          pass
test3 20          fail
      3           pass
      5:1:50      pass

La concatenación permanece sin cambios:

s = pd.concat(dfs.values(), axis=1, keys=dfs.keys())

Ahora primero aplano el índice múltiple ...

s = s.reset_index()

    name      index      0      1
                    status status
0  test1          1   fail   fail
1  test1  1 2 14 56   fail   fail
2  test1         11   pass   pass
3  test1          2   fail   fail
4  test1         22   fail    NaN
5  test1         33   pass   pass
6  test3          2    NaN   fail
7  test3         20   fail   pass
8  test3          3   pass   fail
9  test3     5:1:50   pass    NaN

... y luego aplique la clasificación personalizada y restablezca el índice múltiple:

s = (s.assign(_tmpIdx=s['index'].str.extract(r'([\d]+)').astype(int))
          .sort_values(['name', '_tmpIdx'])
          .set_index(['name', 'index'])
          .drop(['_tmpIdx'],axis=1)
      )

Esto me da el resultado que quiero:

                     0      1
                status status
name  index                  
test1 1           fail   fail
      1 2 14 56   fail   fail
      2           fail   fail
      11          pass   pass
      22          fail    NaN
      33          pass   pass
test3 2            NaN   fail
      3           pass   fail
      5:1:50      pass    NaN
      20          fail   pass

No estoy seguro de si esta es la forma más elegante de hacer este tipo de cosas. ¡Pero funciona! Por supuesto que estoy abierto a mejoras :)

0
twil 4 dic. 2020 a las 16:12

Intente concatenar (concat ) con ignore_index = True.

-1
janw 4 dic. 2020 a las 16:25

Si no necesita que la columna 'índice' sea el índice real, puede fusionar los dos marcos de datos originales en ['nombre', 'índice']:

df.merge(df1,on=['name','index'],how='outer').sort_values('name').reset_index(drop=True)

#     name      index  status_x  status_y
# 0  test1          1     fail     fail
# 1  test1         11     pass     pass
# 2  test1  1 2 14 56     fail     fail
# 3  test1         33     pass     pass
# 4  test1          2     fail     fail
# 5  test1         22     fail      NaN
# 6  test3          3     pass     fail
# 7  test3         20     fail     pass
# 8  test3     5:1:50     pass      NaN

0
user6386471 4 dic. 2020 a las 11:08