two strings data concatenate columns column python pandas

strings - python dataframe concat columns



¿Para qué sirven los argumentos de ''niveles'', ''claves'' y nombres en la función concat de Pandas? (1)

En el proceso de responder esta pregunta por mí mismo, aprendí muchas cosas y quería armar un catálogo de ejemplos y algunas explicaciones.

La respuesta específica al punto del argumento de los levels llegará al final.

pandas.concat : el manual que falta

Enlace a la documentación actual

Importaciones y objetos definitorios

import pandas as pd d1 = pd.DataFrame(dict(A=.1, B=.2, C=.3), index=[2, 3]) d2 = pd.DataFrame(dict(B=.4, C=.5, D=.6), index=[1, 2]) d3 = pd.DataFrame(dict(A=.7, B=.8, D=.9), index=[1, 3]) s1 = pd.Series([1, 2], index=[2, 3]) s2 = pd.Series([3, 4], index=[1, 2]) s3 = pd.Series([5, 6], index=[1, 3])

Argumentos

objs

El primer argumento que encontramos es objs :

objs : una secuencia o mapeo de objetos Series, DataFrame o Panel Si se pasa un dict, las claves ordenadas se utilizarán como argumento de claves, a menos que se pase, en cuyo caso se seleccionarán los valores (ver más abajo). Cualquier objeto None se eliminará en silencio a menos que todos sean None, en cuyo caso se generará un ValueError

  • Normalmente vemos esto usado con una lista de objetos Series o DataFrame .
  • dict que dict puede ser muy útil.
  • Los generadores también se pueden usar y pueden ser útiles cuando se usa el map como en el map(f, list_of_df)

Por ahora, seguiremos con una lista de algunos de los objetos DataFrame y Series definidos anteriormente. MultiIndex cómo se pueden aprovechar los diccionarios para dar resultados MultiIndex muy útiles MultiIndex adelante.

pd.concat([d1, d2]) A B C D 2 0.1 0.2 0.3 NaN 3 0.1 0.2 0.3 NaN 1 NaN 0.4 0.5 0.6 2 NaN 0.4 0.5 0.6

axis

El segundo argumento que encontramos es el axis cuyo valor predeterminado es 0 :

eje : {0 / ''índice'', 1 / ''columnas''}, predeterminado 0 El eje para concatenar.

Dos DataFrame s con axis=0 (apilado)

Para valores de 0 o index queremos decir: "Alinear a lo largo de las columnas y agregar al índice".

Como se muestra arriba, donde usamos axis=0 , porque 0 es el valor predeterminado, y vemos que el índice de d2 extiende el índice de d1 pesar de la superposición del valor 2 :

pd.concat([d1, d2], axis=0) A B C D 2 0.1 0.2 0.3 NaN 3 0.1 0.2 0.3 NaN 1 NaN 0.4 0.5 0.6 2 NaN 0.4 0.5 0.6

Dos DataFrame s con axis=1 (lado a lado)

Para los valores 1 o columns queremos decir: "Alinear a lo largo del índice y agregar a las columnas",

pd.concat([d1, d2], axis=1) A B C B C D 1 NaN NaN NaN 0.4 0.5 0.6 2 0.1 0.2 0.3 0.4 0.5 0.6 3 0.1 0.2 0.3 NaN NaN NaN

Podemos ver que el índice resultante es la unión de índices y las columnas resultantes son la extensión de columnas de d1 por las columnas de d2 .

Dos (o tres) Series con axis=0 (apiladas)

Al combinar pandas.Series largo del axis=0 , obtenemos un pandas.Series . El nombre de la Series resultante será None menos que todas las Series combinadas tengan el mismo nombre. Preste atención al ''Name: A'' cuando imprimamos la Series resultante. Cuando no está presente, podemos suponer que el nombre de la Series es None .

| | | pd.concat( | pd.concat( | pd.concat( | [s1.rename(''A''), pd.concat( | [s1.rename(''A''), | [s1.rename(''A''), | s2.rename(''B''), [s1, s2]) | s2]) | s2.rename(''A'')]) | s3.rename(''A'')]) -------------- | --------------------- | ---------------------- | ---------------------- 2 1 | 2 1 | 2 1 | 2 1 3 2 | 3 2 | 3 2 | 3 2 1 3 | 1 3 | 1 3 | 1 3 2 4 | 2 4 | 2 4 | 2 4 dtype: int64 | dtype: int64 | Name: A, dtype: int64 | 1 5 | | | 3 6 | | | dtype: int64

Dos (o tres) Series con axis=1 (lado a lado)

Al combinar pandas.Series largo de axis=1 , es el atributo de name que nos referimos para inferir el nombre de una columna en el pandas.DataFrame resultante.

| | pd.concat( | pd.concat( | [s1.rename(''X''), pd.concat( | [s1.rename(''X''), | s2.rename(''Y''), [s1, s2], axis=1) | s2], axis=1) | s3.rename(''Z'')], axis=1) ---------------------- | --------------------- | ------------------------------ 0 1 | X 0 | X Y Z 1 NaN 3.0 | 1 NaN 3.0 | 1 NaN 3.0 5.0 2 1.0 4.0 | 2 1.0 4.0 | 2 1.0 4.0 NaN 3 2.0 NaN | 3 2.0 NaN | 3 2.0 NaN 6.0

Series mixtas y DataFrame con axis=0 (apilado)

Cuando se realiza una concatenación de una Series y un DataFrame largo del axis=0 , convertimos todas las Series en un DataFrame una sola columna.

Tenga especial en cuenta que esta es una concatenación a lo largo del axis=0 ; eso significa extender el índice (filas) mientras se alinean las columnas. En los ejemplos a continuación, vemos que el índice se convierte en [2, 3, 2, 3] que es un agregado indiscriminado de índices. Las columnas no se superponen a menos que fuerce el nombre de la columna Series con el argumento to_frame :

pd.concat( | [s1.to_frame(), d1]) | pd.concat([s1, d1]) ------------------------- | --------------------- 0 A B C | 0 A B C 2 1.0 NaN NaN NaN | 2 1.0 NaN NaN NaN 3 2.0 NaN NaN NaN | 3 2.0 NaN NaN NaN 2 NaN 0.1 0.2 0.3 | 2 NaN 0.1 0.2 0.3 3 NaN 0.1 0.2 0.3 | 3 NaN 0.1 0.2 0.3

Puede ver que los resultados de pd.concat([s1, d1]) son los mismos que si hubiera realizado to_frame mismo el to_frame .

Sin embargo, puedo controlar el nombre de la columna resultante con un parámetro a to_frame . Cambiar el nombre de la Series con el método de rename no controla el nombre de la columna en el DataFrame resultante.

# Effectively renames | | # `s1` but does not align | # Does not rename. So | # Renames to something # with columns in `d1` | # Pandas defaults to `0` | # that does align with `d1` pd.concat( | pd.concat( | pd.concat( [s1.to_frame(''X''), d1]) | [s1.rename(''X''), d1]) | [s1.to_frame(''B''), d1]) ---------------------------- | -------------------------- | ---------------------------- A B C X | 0 A B C | A B C 2 NaN NaN NaN 1.0 | 2 1.0 NaN NaN NaN | 2 NaN 1.0 NaN 3 NaN NaN NaN 2.0 | 3 2.0 NaN NaN NaN | 3 NaN 2.0 NaN 2 0.1 0.2 0.3 NaN | 2 NaN 0.1 0.2 0.3 | 2 0.1 0.2 0.3 3 0.1 0.2 0.3 NaN | 3 NaN 0.1 0.2 0.3 | 3 0.1 0.2 0.3

Series mixtas y DataFrame con axis=1 (lado a lado)

Esto es bastante intuitivo. Series nombre de la columna Series se predetermina a una enumeración de dichos objetos Series cuando un atributo de name no está disponible.

| pd.concat( pd.concat( | [s1.rename(''X''), [s1, d1], | s2, s3, d1], axis=1) | axis=1) ------------------- | ------------------------------- 0 A B C | X 0 1 A B C 2 1 0.1 0.2 0.3 | 1 NaN 3.0 5.0 NaN NaN NaN 3 2 0.1 0.2 0.3 | 2 1.0 4.0 NaN 0.1 0.2 0.3 | 3 2.0 NaN 6.0 0.1 0.2 0.3

join

El tercer argumento es join que describe si la fusión resultante debería ser una fusión externa (por defecto) o una fusión interna.

unirse : {''interno'', ''externo''}, predeterminado ''externo''
Cómo manejar índices en otros ejes.

Resulta que no hay una opción left o right , ya que pd.concat puede manejar más de dos objetos para fusionar.

En el caso de d1 y d2 , las opciones se ven así:

outer

pd.concat([d1, d2], axis=1, join=''outer'') A B C B C D 1 NaN NaN NaN 0.4 0.5 0.6 2 0.1 0.2 0.3 0.4 0.5 0.6 3 0.1 0.2 0.3 NaN NaN NaN

inner

pd.concat([d1, d2], axis=1, join=''inner'') A B C B C D 2 0.1 0.2 0.3 0.4 0.5 0.6

join_axes

El cuarto argumento es lo que nos permite hacer nuestra fusión left y más.

join_axes : lista de objetos Index
Índices específicos a utilizar para los otros ejes n - 1 en lugar de realizar la lógica de conjunto interno / externo.

Combinación izquierda

pd.concat([d1, d2, d3], axis=1, join_axes=[d1.index]) A B C B C D A B D 2 0.1 0.2 0.3 0.4 0.5 0.6 NaN NaN NaN 3 0.1 0.2 0.3 NaN NaN NaN 0.7 0.8 0.9

Fusión correcta

pd.concat([d1, d2, d3], axis=1, join_axes=[d3.index]) A B C B C D A B D 1 NaN NaN NaN 0.4 0.5 0.6 0.7 0.8 0.9 3 0.1 0.2 0.3 NaN NaN NaN 0.7 0.8 0.9

ignore_index

ignore_index : boolean, por defecto False
Si es verdadero, no use los valores de índice a lo largo del eje de concatenación. El eje resultante se etiquetará como 0, ..., n - 1. Esto es útil si está concatenando objetos donde el eje de concatenación no tiene información de indexación significativa. Tenga en cuenta que los valores de índice en los otros ejes aún se respetan en la unión.

Al igual que cuando apilo d1 sobre d2 , si no me importan los valores del índice, podría restablecerlos o ignorarlos.

| pd.concat( | pd.concat( | [d1, d2], | [d1, d2] pd.concat([d1, d2]) | ignore_index=True) | ).reset_index(drop=True) --------------------- | ----------------------- | ------------------------- A B C D | A B C D | A B C D 2 0.1 0.2 0.3 NaN | 0 0.1 0.2 0.3 NaN | 0 0.1 0.2 0.3 NaN 3 0.1 0.2 0.3 NaN | 1 0.1 0.2 0.3 NaN | 1 0.1 0.2 0.3 NaN 1 NaN 0.4 0.5 0.6 | 2 NaN 0.4 0.5 0.6 | 2 NaN 0.4 0.5 0.6 2 NaN 0.4 0.5 0.6 | 3 NaN 0.4 0.5 0.6 | 3 NaN 0.4 0.5 0.6

Y cuando se usa axis=1 :

| pd.concat( | [d1, d2], axis=1, pd.concat([d1, d2], axis=1) | ignore_index=True) ------------------------------- | ------------------------------- A B C B C D | 0 1 2 3 4 5 1 NaN NaN NaN 0.4 0.5 0.6 | 1 NaN NaN NaN 0.4 0.5 0.6 2 0.1 0.2 0.3 0.4 0.5 0.6 | 2 0.1 0.2 0.3 0.4 0.5 0.6 3 0.1 0.2 0.3 NaN NaN NaN | 3 0.1 0.2 0.3 NaN NaN NaN

keys

Podemos pasar una lista de valores escalares o tuplas para asignar valores tuples o escalares al MultiIndex correspondiente. La longitud de la lista aprobada debe ser la misma que la cantidad de elementos que se concatenan.

teclas : secuencia, por defecto Ninguno
Si pasaron varios niveles, deberían contener tuplas. Construya el índice jerárquico usando las claves pasadas como el nivel más externo

axis=0

Al concatenar objetos Series largo del axis=0 (extendiendo el índice).

Esas claves se convierten en un nuevo nivel inicial de un objeto MultiIndex en el atributo de índice.

# length 3 length 3 # length 2 length 2 # /--------/ /-----------/ # /----/ /------/ pd.concat([s1, s2, s3], keys=[''A'', ''B'', ''C'']) pd.concat([s1, s2], keys=[''A'', ''B'']) ---------------------------------------------- ------------------------------------- A 2 1 A 2 1 3 2 3 2 B 1 3 B 1 3 2 4 2 4 C 1 5 dtype: int64 3 6 dtype: int64

Sin embargo, podemos usar más que valores escalares en el argumento de keys para crear un MultiIndex aún más MultiIndex . Aquí pasamos tuples de longitud 2 antepuestas a dos nuevos niveles de un MultiIndex :

pd.concat( [s1, s2, s3], keys=[(''A'', ''X''), (''A'', ''Y''), (''B'', ''X'')]) ----------------------------------------------- A X 2 1 3 2 Y 1 3 2 4 B X 1 5 3 6 dtype: int64

axis=1

Es un poco diferente cuando se extiende a lo largo de las columnas. Cuando usamos axis=0 (ver arriba) nuestras keys actuaban como niveles de MultiIndex además del índice existente. Para axis=1 , nos estamos refiriendo a un eje que los objetos Series no tienen, es decir, el atributo de columns .

Variaciones de dos Series axis=1

Observe que nombrar s1 y s2 importante siempre que no se pasen keys , pero se anula si se pasan keys .

| | | pd.concat( | pd.concat( | pd.concat( | [s1.rename(''U''), pd.concat( | [s1, s2], | [s1.rename(''U''), | s2.rename(''V'')], [s1, s2], | axis=1, | s2.rename(''V'')], | axis=1, axis=1) | keys=[''X'', ''Y'']) | axis=1) | keys=[''X'', ''Y'']) -------------- | --------------------- | ---------------------- | ---------------------- 0 1 | X Y | U V | X Y 1 NaN 3.0 | 1 NaN 3.0 | 1 NaN 3.0 | 1 NaN 3.0 2 1.0 4.0 | 2 1.0 4.0 | 2 1.0 4.0 | 2 1.0 4.0 3 2.0 NaN | 3 2.0 NaN | 3 2.0 NaN | 3 2.0 NaN MultiIndex con Series y axis=1

pd.concat( [s1, s2], axis=1, keys=[(''W'', ''X''), (''W'', ''Y'')]) ----------------------------------- W X Y 1 NaN 3.0 2 1.0 4.0 3 2.0 NaN Dos DataFrame con axis=1

Al igual que con los ejemplos axis=0 , las keys agregan niveles a un MultiIndex , pero esta vez al objeto almacenado en el atributo de columns .

pd.concat( | pd.concat( [d1, d2], | [d1, d2], axis=1, | axis=1, keys=[''X'', ''Y'']) | keys=[(''First'', ''X''), (''Second'', ''X'')]) ------------------------------- | -------------------------------------------- X Y | First Second A B C B C D | X X 1 NaN NaN NaN 0.4 0.5 0.6 | A B C B C D 2 0.1 0.2 0.3 0.4 0.5 0.6 | 1 NaN NaN NaN 0.4 0.5 0.6 3 0.1 0.2 0.3 NaN NaN NaN | 2 0.1 0.2 0.3 0.4 0.5 0.6 | 3 0.1 0.2 0.3 NaN NaN NaN Series y DataFrame con axis=1

Esto es complicado En este caso, un valor de clave escalar no puede actuar como el único nivel de índice para el objeto Series cuando se convierte en una columna mientras que también actúa como el primer nivel de un MultiIndex para el DataFrame . Entonces, Pandas usará nuevamente el atributo de name del objeto Series como fuente del nombre de la columna.

pd.concat( | pd.concat( [s1, d1], | [s1.rename(''Z''), d1], axis=1, | axis=1, keys=[''X'', ''Y'']) | keys=[''X'', ''Y'']) --------------------- | -------------------------- X Y | X Y 0 A B C | Z A B C 2 1 0.1 0.2 0.3 | 2 1 0.1 0.2 0.3 3 2 0.1 0.2 0.3 | 3 2 0.1 0.2 0.3 Limitaciones de keys e MultiIndex MultiIndex.

Pandas solo parece inferir nombres de columna a partir del nombre de la Series , pero no completará los espacios en blanco cuando se realiza una concatenación análoga entre marcos de datos con un número diferente de niveles de columna.

d1_ = pd.concat( [d1], axis=1, keys=[''One'']) d1_ One A B C 2 0.1 0.2 0.3 3 0.1 0.2 0.3

Luego concatene esto con otro marco de datos con solo un nivel en el objeto de columnas y Pandas se negará a intentar hacer tuplas del objeto MultiIndex y combinará todos los marcos de datos como si fuera un solo nivel de objetos, escalares y tuplas.

pd.concat([d1_, d2], axis=1) (One, A) (One, B) (One, C) B C D 1 NaN NaN NaN 0.4 0.5 0.6 2 0.1 0.2 0.3 0.4 0.5 0.6 3 0.1 0.2 0.3 NaN NaN NaN

Pasar un dict lugar de una list

Al pasar un diccionario, pandas.concat usará las claves del diccionario como parámetro de keys .

# axis=0 | # axis=1 pd.concat( | pd.concat( {0: d1, 1: d2}) | {0: d1, 1: d2}, axis=1) ----------------------- | ------------------------------- A B C D | 0 1 0 2 0.1 0.2 0.3 NaN | A B C B C D 3 0.1 0.2 0.3 NaN | 1 NaN NaN NaN 0.4 0.5 0.6 1 1 NaN 0.4 0.5 0.6 | 2 0.1 0.2 0.3 0.4 0.5 0.6 2 NaN 0.4 0.5 0.6 | 3 0.1 0.2 0.3 NaN NaN NaN

levels

Esto se usa junto con el argumento de las keys Cuando los levels se dejan como su valor predeterminado de None , Pandas tomará los valores únicos de cada nivel del MultiIndex resultante y lo usará como el objeto usado en el atributo index.levels resultante.

niveles : lista de secuencias, por defecto Ninguno
Niveles específicos (valores únicos) a utilizar para construir un MultiIndex. De lo contrario, se deducirán de las teclas.

Si Pandas ya infiere cuáles deberían ser estos niveles, ¿qué ventaja hay para especificarlo nosotros mismos? Mostraré un ejemplo y dejaré que pienses en otras razones por las cuales esto podría ser útil.

Ejemplo

Según la documentación, el argumento de levels es una lista de secuencias. Esto significa que podemos usar otros pandas.Index como una de esas secuencias.

Considere el marco de datos df que es la concatenación de d1 , d2 y d3 :

df = pd.concat( [d1, d2, d3], axis=1, keys=[''First'', ''Second'', ''Fourth'']) df First Second Fourth A B C B C D A B D 1 NaN NaN NaN 0.4 0.5 0.6 0.7 0.8 0.9 2 0.1 0.2 0.3 0.4 0.5 0.6 NaN NaN NaN 3 0.1 0.2 0.3 NaN NaN NaN 0.7 0.8 0.9

Los niveles del objeto de columnas son:

print(df, *df.columns.levels, sep=''/n'') Index([''First'', ''Second'', ''Fourth''], dtype=''object'') Index([''A'', ''B'', ''C'', ''D''], dtype=''object'')

Si usamos la sum dentro de un groupby , obtenemos:

df.groupby(axis=1, level=0).sum() First Fourth Second 1 0.0 2.4 1.5 2 0.6 0.0 1.5 3 0.6 2.4 0.0

Pero, ¿qué pasaría si en lugar de [''First'', ''Second'', ''Fourth''] hubiera otras categorías faltantes llamadas Third y Fifth ? ¿Y quería que se incluyeran en los resultados de un groupby agregación? Podemos hacer esto si tuviéramos pandas.CategoricalIndex . Y podemos especificar eso de antemano con el argumento de levels .

Entonces, en su lugar, definamos df como:

cats = [''First'', ''Second'', ''Third'', ''Fourth'', ''Fifth''] lvl = pd.CategoricalIndex(cats, categories=cats, ordered=True) df = pd.concat( [d1, d2, d3], axis=1, keys=[''First'', ''Second'', ''Fourth''], levels=[lvl] ) df First Fourth Second 1 0.0 2.4 1.5 2 0.6 0.0 1.5 3 0.6 2.4 0.0

Pero el primer nivel del objeto de columnas es:

df.columns.levels[0] CategoricalIndex( [''First'', ''Second'', ''Third'', ''Fourth'', ''Fifth''], categories=[''First'', ''Second'', ''Third'', ''Fourth'', ''Fifth''], ordered=True, dtype=''category'')

Y nuestro resumen groupby ve así:

df.groupby(axis=1, level=0).sum() First Second Third Fourth Fifth 1 0.0 1.5 0.0 2.4 0.0 2 0.6 1.5 0.0 0.0 0.0 3 0.6 0.0 0.0 2.4 0.0

names

Esto se usa para nombrar los niveles de un MultiIndex resultante. La longitud de la lista de names debe coincidir con el número de niveles en el MultiIndex resultante.

nombres : lista, predeterminado Ninguno
Nombres para los niveles en el índice jerárquico resultante

# axis=0 | # axis=1 pd.concat( | pd.concat( [d1, d2], | [d1, d2], keys=[0, 1], | axis=1, keys=[0, 1], names=[''lvl0'', ''lvl1'']) | names=[''lvl0'', ''lvl1'']) ----------------------------- | ---------------------------------- A B C D | lvl0 0 1 lvl0 lvl1 | lvl1 A B C B C D 0 2 0.1 0.2 0.3 NaN | 1 NaN NaN NaN 0.4 0.5 0.6 3 0.1 0.2 0.3 NaN | 2 0.1 0.2 0.3 0.4 0.5 0.6 1 1 NaN 0.4 0.5 0.6 | 3 0.1 0.2 0.3 NaN NaN NaN 2 NaN 0.4 0.5 0.6 |

verify_integrity

Documentación autoexplicativa

understand_integrity : boolean, por defecto False
Compruebe si el nuevo eje concatenado contiene duplicados. Esto puede ser muy costoso en relación con la concatenación de datos real.

Debido a que el índice resultante de concatenar d1 y d2 no es único, fallará la verificación de integridad.

pd.concat([d1, d2]) A B C D 2 0.1 0.2 0.3 NaN 3 0.1 0.2 0.3 NaN 1 NaN 0.4 0.5 0.6 2 NaN 0.4 0.5 0.6

Y

pd.concat([d1, d2], verify_integrity=True)

> ValueError: los índices tienen valores superpuestos: [2]

Preguntas

  • ¿Cómo uso pd.concat ?
  • ¿Para qué sirve el argumento de los levels ?
  • ¿Para qué sirve el argumento de las keys ?
  • ¿Hay algunos ejemplos para ayudar a explicar cómo usar todos los argumentos?

La función concat Pandas es la navaja suiza de las empresas de servicios públicos fusionadas. La variedad de situaciones en las que es útil son numerosas. La documentación existente deja de lado algunos detalles sobre algunos de los argumentos opcionales. Entre ellos están los levels y argumentos keys . Me propuse averiguar qué hacen esos argumentos.

pd.concat una pregunta que actuará como puerta de entrada a muchos aspectos de pd.concat .

Considere los marcos de datos d1 , d2 y d3 :

import pandas as pd d1 = pd.DataFrame(dict(A=.1, B=.2, C=.3), [2, 3]) d2 = pd.DataFrame(dict(B=.4, C=.5, D=.6), [1, 2]) d3 = pd.DataFrame(dict(A=.7, B=.8, D=.9), [1, 3])

Si tuviera que concatenar esto junto con

pd.concat([d1, d2, d3], keys=[''d1'', ''d2'', ''d3''])

Obtengo el resultado esperado con un pandas.MultiIndex para mi objeto de columns :

A B C D d1 2 0.1 0.2 0.3 NaN 3 0.1 0.2 0.3 NaN d2 1 NaN 0.4 0.5 0.6 2 NaN 0.4 0.5 0.6 d3 1 0.7 0.8 NaN 0.9 3 0.7 0.8 NaN 0.9

Sin embargo, quería usar la documentación de argumentación de levels :

niveles : lista de secuencias, por defecto Ninguno. Niveles específicos (valores únicos) a utilizar para construir un MultiIndex. De lo contrario, se deducirán de las teclas.

Entonces pasé

pd.concat([d1, d2, d3], keys=[''d1'', ''d2'', ''d3''], levels=[[''d1'', ''d2'']])

Y consigue un KeyError

ValueError: Key d3 not in level Index([''d1'', ''d2''], dtype=''object'')

Esto tiene sentido. Los niveles que pasé fueron inadecuados para describir los niveles necesarios indicados por las teclas. Si no hubiera pasado nada, como hice anteriormente, los niveles se infieren (como se indica en la documentación). Pero, ¿de qué otra manera puedo usar este argumento para tener un mejor efecto?

Si probé esto en su lugar:

pd.concat([d1, d2, d3], keys=[''d1'', ''d2'', ''d3''], levels=[[''d1'', ''d2'', ''d3'']])

Yo y obtuve los mismos resultados que arriba. Pero cuando agrego un valor más a los niveles,

df = pd.concat([d1, d2, d3], keys=[''d1'', ''d2'', ''d3''], levels=[[''d1'', ''d2'', ''d3'', ''d4'']])

MultiIndex con el mismo marco de datos de aspecto, pero el MultiIndex resultante tiene un nivel no utilizado.

df.index.levels[0] Index([''d1'', ''d2'', ''d3'', ''d4''], dtype=''object'')

Entonces, ¿cuál es el punto del argumento de level y debería usar las keys diferente?

Estoy usando Python 3.6 y Pandas 0.22.