首页 > 解决方案 > 将多索引 daframe 与系列连接起来

问题描述

我有一个多索引数据框,其中包含用于训练机器学习模型的功能。

X_train

win      -1           0           1
res       A     B     A     B     A     B
id  pos   
foo 0     0.1   0.2   0.3   0.1   0.5   0.6
    1     0.3   0.7   0.2   0.1   0.4   0.6
    2     0.1   0.2   0.3   0.1   0.5   0.6
bar 0     0.1   0.0   0.3   0.1   0.5   0.6
    1     0.1   0.2   0.0   0.3   0.9   0.6
    2     0.1   0.2   0.3   0.1   0.5   0.6

以及带有类标签的熊猫系列(或数据框)

y_train
    
id  pos   
foo 0     0
    1     2
    2     1
bar 0     0
    1     0
    2     1
Name: Class

我想连接它们并获得

full_train

win      -1           0           1          Class
res       A     B     A     B     A     B    
id  pos   
foo 0     0.1   0.2   0.3   0.1   0.5   0.6  0  
    1     0.3   0.7   0.2   0.1   0.4   0.6  2
    2     0.1   0.2   0.3   0.1   0.5   0.6  1
bar 0     0.1   0.0   0.3   0.1   0.5   0.6  0
    1     0.1   0.2   0.0   0.3   0.9   0.6  0
    2     0.1   0.2   0.3   0.1   0.5   0.6  1

但如果我这样做full_train = pd.concat([X_train, y_train], axis=1),我会获得:

          (-1,A)(-1,B)(0,A) (0,B)(1,A) (1,B) Class
id  pos   
foo 0     0.1   0.2   0.3   0.1   0.5   0.6  0  
    1     0.3   0.7   0.2   0.1   0.4   0.6  2
    2     0.1   0.2   0.3   0.1   0.5   0.6  1
bar 0     0.1   0.0   0.3   0.1   0.5   0.6  0
    1     0.1   0.2   0.0   0.3   0.9   0.6  0
    2     0.1   0.2   0.3   0.1   0.5   0.6  1

是否有可能获得我的预期输出?Pandas 是否支持这种混合列既是多索引又是单索引?

标签: pythonpandas

解决方案


熊猫确实支持这个!但是,您需要了解一些隐藏知识以进行连接以使其按预期工作。

对于以下示例,我将使用此数据框和此系列y_train

import pandas as pd
import numpy as np

columns = pd.MultiIndex.from_product([[-1, 0, 1], ["A", "B"]], names=["win", "res"])
index = pd.MultiIndex.from_product([["foo", "bar"], [0, 1, 2]], names=["id", "pos"])

m = len(columns)
n = len(index)

data = np.random.randint(5, size=(m * n)).reshape(m, n)
df = pd.DataFrame(data, index=index, columns=columns)

print(df)
win     -1     0     1   
res      A  B  A  B  A  B
id  pos                  
foo 0    3  4  4  4  4  1
    1    4  1  4  3  1  0
    2    0  4  2  2  3  2
bar 0    3  2  1  4  1  1
    1    0  3  1  1  0  3
    2    2  0  2  1  0  1

标签:

y_train = pd.Series(np.random.randint(3, size=m), index=index, name="class")

print(y_train)
id   pos
foo  0      2
     1      1
     2      2
bar  0      1
     1      1
     2      2
Name: class, dtype: int32

如果您y_train是系列,则根本不需要使用concat。最直接的方法是将您的y_train系列简单地分配到原始数据框中。

new_df = df.copy() # preserving original dataframe for use in later examples
new_df["class"] = y_train

print(new_df)
win     -1     0     1    class
res      A  B  A  B  A  B      
id  pos                        
foo 0    3  4  4  4  4  1     2
    1    4  1  4  3  1  0     1
    2    0  4  2  2  3  2     2
bar 0    3  2  1  4  1  1     1
    1    0  3  1  1  0  3     1
    2    2  0  2  1  0  1     2

# We can select our "class" column and get its `Series` out via:
print(new_df["class"])

id   pos
foo  0      2
     1      1
     2      2
bar  0      1
     1      1
     2      2
Name: class, dtype: int32

# Then to access our other columns, we'll need to supply a key as a tuple:
print(new_df[(-1, "A")])

id   pos
foo  0      4
     1      1
     2      4
bar  0      2
     1      3
     2      0
Name: (-1, B), dtype: int32

y_train如果你是一个数据框而不是一个系列,现在事情会变得有点棘手。但是,这种方法也可以解决您的concat问题。您需要做的就是将标签数据框的 ( y_train) 列索引替换为MultiIndex与您尝试连接的其他数据框具有相同级别数的索引。您需要使用空级别来实现这一点(空级别是唯一值是空字符串的级别。NOT None 或 NaN)

new_ytrain = y_train.to_frame() # promote y_train to a dataframe

# In order to feed this into concat, we'll need to give our dataframe a columnar multiindex
# This multiindex essentially has an empty second level
new_ytrain.columns = pd.MultiIndex.from_product([new_ytrain.columns, [""]])

print(new_ytrain)
        class
             
id  pos      
foo 0       2
    1       1
    2       2
bar 0       1
    1       1
    2       2

在我们的y_train数据框表示中,您会看到我们的列标题和实际数据之间有一个空格。这代表我们插入的空级别。现在我们的原始数据框和我们y_train都有一个MultiIndex具有 2 个级别的列,我们可以按预期使用 concat 没有问题:

out = pd.concat([df, new_ytrain], axis=1)

print(out)
win     -1     0     1    class
res      A  B  A  B  A  B      
id  pos                        
foo 0    3  4  4  4  4  1     2
    1    4  1  4  3  1  0     1
    2    0  4  2  2  3  2     2
bar 0    3  2  1  4  1  1     1
    1    0  3  1  1  0  3     1
    2    2  0  2  1  0  1     2

与分配方法一样,我们可以轻松地在此数据框中选择我们的“类”列。

print(out["class"])

id   pos
foo  0      2
     1      1
     2      2
bar  0      1
     1      1
     2      2
Name: class, dtype: int32

推荐阅读