读取Excel文件并合并熊猫中未命名的列

问题描述

我正在尝试使用Pandas从调查结果表中读取excel文件(在有参与者的行上),但是我将许多变量分成多个列,就像这样

>>> df.columns
Index([ ...,'Age','Unnamed: 12','Unnamed: 13','Unnamed: 14','Unnamed: 15','Unnamed: 16',...],dtype='object',length=256)

其中'Age'之后和下一个命名列之前的每个未命名列仅包含Age变量的值,该值对应于该多项选择题中的一个选择。 如何获得同一列下的所有“年龄”值?

编辑:df.head(5).to_dict()输出示例:

{...,'Gender': {0: 'M',1: 'M',2: 'M',3: nan,4: nan},'Unnamed: 10': {0: 'F',1: nan,2: nan,3: 'F',4: 'F'},'Age': {0: 25.0,2: 25.0,'Unnamed: 12': {0: 26.0,3: 26.0,'Unnamed: 13': {0: 27.0,'Unnamed: 14': {0: 28.0,4: 28.0},'Unnamed: 15': {0: 29.0,'Unnamed: 16': {0: 30.0,...}

解决方法

第一步,

让我们删除Unnamed:列,然后向前填充值。

df.columns = df.columns.to_series().replace('Unnamed:\s\d+',np.nan,regex=True).ffill().values

print(df)

  Gender Gender   Age   Age   Age   Age   Age   Age
0      M      F  25.0  26.0  27.0  28.0  29.0  30.0
1      M    NaN   NaN   NaN   NaN   NaN   NaN   NaN
2      M    NaN  25.0   NaN   NaN   NaN   NaN   NaN
3    NaN      F   NaN  26.0   NaN   NaN   NaN   NaN
4    NaN      F   NaN   NaN   NaN  28.0   NaN   NaN

然后我们可以重塑您的数据框并创建一个新索引,以便我们unstack

s = df.T.agg(list,1).explode().dropna().to_frame()

df1 = s.set_index(s.groupby(level=0).cumcount(),append=True).unstack(0)


print(df1)

  Age Gender
0  25      M
1  25      M
2  26      M
3  26      F
4  27      F
5  28      F
6  28    NaN
7  29    NaN
8  30    NaN

另一种方法是为您的列创建一个多索引,这样可以更好地保留原始索引。

df.columns = df.columns.to_series()\
               .replace('Unnamed:\s\d+',regex=True).ffill().values
df.columns = pd.MultiIndex.from_tuples([(x,y)for x,y in 
                 zip(df.columns,df.columns.to_series().groupby(level=0).cumcount())])


print(df)

  Gender        Age                              
       0    1     0     1     2     3     4     5
0      M    F  25.0  26.0  27.0  28.0  29.0  30.0
1      M  NaN   NaN   NaN   NaN   NaN   NaN   NaN
2      M  NaN  25.0   NaN   NaN   NaN   NaN   NaN
3    NaN    F   NaN  26.0   NaN   NaN   NaN   NaN
4    NaN    F   NaN   NaN   NaN  28.0   NaN   NaN


print(df.stack(1))

      Age Gender
0 0  25.0      M
  1  26.0      F
  2  27.0    NaN
  3  28.0    NaN
  4  29.0    NaN
  5  30.0    NaN
1 0   NaN      M
2 0  25.0      M
3 1  26.0      F
4 1   NaN      F
  3  28.0    NaN
,

此解决方案有点难看,但是应该可以。本质上,您是数据框的子集,以挑选出与特定问题关联的所有列。其次,您使用函数来选择每一行中不是NaN的第一个值。

df = df.drop([0])  # Drop first row,contains column headings

# This function treats each row as a Series. It then gets the value
# of the first defined cell,and returns it. Or,if the row is all
# None,it returns None.
def get_first_valid_from_row(x):
    if x.first_valid_index() is None:
        return None
    else:
        return x[x.first_valid_index()]

new_df = pd.DataFrame()

# Get gender-related columns
gender_subset_df = df[["Gender","Unnamed: 10"]]
new_df["Gender"] = gender_subset_df.apply(get_first_valid_from_row,axis=1)

# Get age-related columns
age_subset_df = df[["Age","Unnamed: 12","Unnamed: 13","Unnamed: 14","Unnamed: 15","Unnamed: 16"]]
new_df["Age"] = age_subset_df.apply(get_first_valid_from_row,axis=1)
print(new_df)

我得到以下结果:

  Gender   Age
1      M   NaN
2      M  25.0
3      F  26.0
4      F  28.0

第一行的年龄无效,但是从原始数据来看,数据集中的第一个人似乎没有选择任何年龄。

使用此方法记入@EdChum