Cómo fusionar una serie y un DataFrame

82

Si usted vino aquí en busca de información sobre cómo combinar una DataFramey Seriesen el índice , por favor vaya a esta respuesta .

La intención original del OP era preguntar cómo asignar elementos de serie como columnas a otro DataFrame . Si está interesado en conocer la respuesta a esto, mire la respuesta aceptada por EdChum.


Lo mejor que se me ocurre es

df = pd.DataFrame({'a':[1, 2], 'b':[3, 4]})  # see EDIT below
s = pd.Series({'s1':5, 's2':6})

for name in s.index:
    df[name] = s[name]

   a  b  s1  s2
0  1  3   5   6
1  2  4   5   6

¿Alguien puede sugerir una mejor sintaxis / un método más rápido?

Mis intentos:

df.merge(s)
AttributeError: 'Series' object has no attribute 'columns'

y

df.join(s)
ValueError: Other Series must have a name

EDITAR Las dos primeras respuestas publicadas destacaron un problema con mi pregunta, así que use lo siguiente para construir df:

df = pd.DataFrame({'a':[np.nan, 2, 3], 'b':[4, 5, 6]}, index=[3, 5, 6])

con el resultado final

    a  b  s1  s2
3 NaN  4   5   6
5   2  5   5   6
6   3  6   5   6
Nathan Lloyd
fuente

Respuestas:

25

Puede construir un marco de datos a partir de la serie y luego fusionarlo con el marco de datos. Por lo tanto, especifica los datos como los valores, pero los multiplica por la longitud, establece las columnas en el índice y establece los parámetros para left_index y right_index en True:

In [27]:

df.merge(pd.DataFrame(data = [s.values] * len(s), columns = s.index), left_index=True, right_index=True)
Out[27]:
   a  b  s1  s2
0  1  3   5   6
1  2  4   5   6

EDITAR para la situación en la que desea que el índice de su df construido de la serie use el índice del df, entonces puede hacer lo siguiente:

df.merge(pd.DataFrame(data = [s.values] * len(df), columns = s.index, index=df.index), left_index=True, right_index=True)

Esto supone que los índices coinciden con la longitud.

EdChum
fuente
163

Actualización
Desde v0.24.0 en adelante, puede fusionar DataFrame y Series siempre que se nombre la Serie.

df.merge(s.rename('new'), left_index=True, right_index=True)
# If series is already named,
# df.merge(s, left_index=True, right_index=True)

Hoy en día, simplemente puede convertir la Serie en un DataFrame con to_frame () . Entonces (si se une al índice):

df.merge(s.to_frame(), left_index=True, right_index=True)
Nicolás Morley
fuente
5
Usando las definiciones de la pregunta de dfy s, esta respuesta me devuelve un marco de datos vacío, no el resultado solicitado en la pregunta. No queremos hacer coincidir en el índice; queremos transmitir los svalores a todas las filas de df.
CPBL
2
Esto está resolviendo un problema diferente: "dado un DataFrame y una serie, ¿cómo pueden fusionarse en el índice". La pregunta de OP era "asignar cada elemento de una serie como una nueva columna en un DataFrame".
cs95
5

He aquí una forma:

df.join(pd.DataFrame(s).T).fillna(method='ffill')

Para analizar lo que sucede aquí ...

pd.DataFrame(s).Tcrea un DataFrame de una fila sque se ve así:

   s1  s2
0   5   6

A continuación, joinconcatena este nuevo marco con df:

   a  b  s1  s2
0  1  3   5   6
1  2  4 NaN NaN

Por último, los NaNvalores en el índice 1 se rellenan con los valores anteriores en la columna usando fillnael ffillargumento forward-fill ( ):

   a  b  s1  s2
0  1  3   5   6
1  2  4   5   6

Para evitar el uso fillna, es posible usar pd.concatpara repetir las filas del DataFrame construido a partir de s. En este caso, la solución general es:

df.join(pd.concat([pd.DataFrame(s).T] * len(df), ignore_index=True))

Aquí hay otra solución para abordar el desafío de indexación planteado en la pregunta editada:

df.join(pd.DataFrame(s.repeat(len(df)).values.reshape((len(df), -1), order='F'), 
        columns=s.index, 
        index=df.index))

sse transforma en un DataFrame repitiendo los valores y remodelando (especificando el orden 'Fortran'), y también pasando los nombres de columna y el índice apropiados. A continuación, se une este nuevo DataFrame df.

Alex Riley
fuente
Buen resumen, una advertencia es que cualquier NaN que ya esté en df también se completará.
Nathan Lloyd
@Nonth Gracias y buen punto. He editado para incluir una alternativa que evite rellenar NaNvalores.
Alex Riley
Lo que sucedió con la respuesta original de EdChums afecta esta respuesta revisada. Si construyo el df con, digamos,, index=[3, 5]las nuevas columnas contienen nan después de su comando.
Nathan Lloyd
@Nonth Editado de nuevo! Ahora debería cumplir con sus nuevos requisitos.
Alex Riley
su respuesta es 20 veces más rápida, pero sigue siendo una diferencia de ~ 100 ms con df en 1e5 filas. Mi bucle for es horriblemente lento. Por cierto, en su respuesta 2debería ser len(df)de aplicación general.
Nathan Lloyd
0

Si pudiera sugerirle que configure sus marcos de datos como este (autoindexación):

df = pd.DataFrame({'a':[np.nan, 1, 2], 'b':[4, 5, 6]})

luego puede configurar sus valores s1 y s2 así (usando shape () para devolver el número de filas de df):

s = pd.DataFrame({'s1':[5]*df.shape[0], 's2':[6]*df.shape[0]})

entonces el resultado que desea es fácil:

display (df.merge(s, left_index=True, right_index=True))

Alternativamente, simplemente agregue los nuevos valores a su marco de datos df:

df = pd.DataFrame({'a':[nan, 1, 2], 'b':[4, 5, 6]})
df['s1']=5
df['s2']=6
display(df)

Ambos regresan:

     a  b  s1  s2
0  NaN  4   5   6
1  1.0  5   5   6
2  2.0  6   5   6

Si tiene otra lista de datos (en lugar de un solo valor para aplicar), y sabe que está en la misma secuencia que df, por ejemplo:

s1=['a','b','c']

entonces puedes adjuntar esto de la misma manera:

df['s1']=s1

devoluciones:

     a  b s1
0  NaN  4  a
1  1.0  5  b
2  2.0  6  c
James
fuente
0

Puede establecer fácilmente una columna pandas.DataFrame en una constante. Esta constante puede ser un int como en su ejemplo. Si la columna que especifica no está en el df, los pandas crearán una nueva columna con el nombre que especifique. Entonces, después de que se construya su marco de datos, (de su pregunta):

df = pd.DataFrame({'a':[np.nan, 2, 3], 'b':[4, 5, 6]}, index=[3, 5, 6])

Puedes simplemente ejecutar:

df['s1'], df['s2'] = 5, 6

Puede escribir un bucle o comprensión para que haga esto para todos los elementos en una lista de tuplas, o claves y valores en un diccionario, dependiendo de cómo tenga almacenados sus datos reales.

Alex
fuente
0

Si dfes un, pandas.DataFrameentonces df['new_col']= Series list_object of length len(df)agregará el objeto de lista o Serie como una columna nombrada 'new_col'. df['new_col']= scalar(como 5 o 6 en su caso) también funciona y es equivalente adf['new_col']= [scalar]*len(df)

Entonces, un código de dos líneas sirve para el propósito:

df = pd.DataFrame({'a':[1, 2], 'b':[3, 4]})
s = pd.Series({'s1':5, 's2':6})
for x in s.index:    
    df[x] = s[x]

Output: 
   a  b  s1  s2
0  1  3   5   6
1  2  4   5   6
aishik roy chaudhury
fuente