如何将pandas Series转换为带指定多列名的DataFrame
问题描述
以下是复现问题的完整代码:
import numpy as np from sklearn.datasets import load_iris import pandas as pd ### 准备数据 Xy = np.c_[load_iris(return_X_y=True)] mycol = ['x1','x2','x3','x4','group'] df = pd.DataFrame(data=Xy, columns=mycol) dat = df.iloc[:100,:] # 仅选取两类鸢尾花数据 dat['group'] = dat.group.apply(lambda x: 1 if x ==0 else 2) # 两类映射为分组1、2 ### 线性判别分析流程 G1 = dat.iloc[:50,:-1]; x1_bar = G1.mean(); S1 = G1.cov(); n1 = G1.shape[0] G2 = dat.iloc[50:,:-1]; x2_bar = G2.mean(); S2 = G2.cov(); n2 = G2.shape[0] Sp = (n1-1)/(n1+n2-2)*S1 + (n2-1)/(n1+n2-2)*S2 a = np.linalg.inv(Sp).dot(x1_bar-x2_bar); u_bar = (x1_bar + x2_bar)/2 m = a.T.dot(u_bar); print("线性判别边界值为 {} ".format(m)) def my_lda(x): y = a.T.dot(x) pred = 1 if y >= m else 2 return y.round(4), pred xx = dat.iloc[:,:-1] xxa = xx.agg(my_lda, axis=1)
运行代码后得到的xxa是形状为(100,)的pandas.core.series.Series对象,每个元素是包含两个返回值的元组。需求是将xxa转换为100行2列的pd.DataFrame,指定列名为['y','pred'],两次错误尝试如下:
- 第一次尝试直接传入Series构造DataFrame,触发报错:
xxa_df1 = pd.DataFrame(data=xxa, columns=['y','pred'])
ValueError: Shape of passed values is (100, 1), indices imply (100, 2)
- 第二次尝试先将Series转为单列表格再指定列名,得到的结果虽然是100行2列结构,但所有值均为NaN:
xxa2 = xxa.to_frame() # xxa2 = pd.DataFrame(xxa) # 和`xxa.to_frame()`效果完全一致 xxa_df2 = pd.DataFrame(data=xxa2, columns=['y','pred'])
错误原因
- 第一次尝试:存储元组的Series本质是一维结构,每个元组会被pandas识别为单个单元格的值,因此传入后实际只能生成100行1列的表格,和指定的2列要求不匹配,触发形状不兼容报错。
- 第二次尝试:
to_frame()生成的单列表格默认列名是0,后续构造DataFrame时指定['y','pred']列名,pandas会在原表中查找这两个列名对应的数据,查找失败就会全部填充NaN。
正确实现方案
提供三种可直接运行的常用方案,按需选择即可:
- 最高效简洁的写法:将Series中的元组转为二维列表直接构造,可保留原行索引
xxa_df = pd.DataFrame(xxa.tolist(), columns=['y', 'pred'], index=xxa.index)
- 通用兼容写法:逐元素展开为Series后修改列名
xxa_df = xxa.apply(pd.Series) xxa_df.columns = ['y', 'pred']
- 从源头规避转换步骤:修改聚合函数直接返回带列名的Series,
apply运行后直接得到目标结构的DataFrame
def my_lda(x): y = a.T.dot(x) pred = 1 if y >= m else 2 return pd.Series([y.round(4), pred], index=['y','pred']) # 直接执行apply即可得到符合要求的DataFrame xxa_df = xx.apply(my_lda, axis=1)
内容的提问来源于stack exchange,提问作者John Stone
相关产品推荐
相关产品推荐

