微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

python – 在多索引数据中插入一行

我有一个多索引数据帧(行上的多索引),我想在某个行号插入一行.这个问题在( How to insert a row in a Pandas multiindex dataframe?)之前已经被讨论了,但我想解决一些不同的问题

事实上.我解决了它,但它太丑了,我想问是否有更好的方法来做到这一点.

由于pandas中没有insert_row函数,我想通过首先使用切片将我的数据帧复制到某个行到新帧来插入一行,然后追加我要插入的行,然后追加其余的原始数据帧.实际上,这种方法很有效.这是一个例子

首先,我创建一个空数据框

pipeinfo_index = pd.MultiIndex.from_tuples([tuple([None,None])],names=["Label","Side"])
pipeinfoDF = pd.DataFrame(index=pipeinfo_index,columns=[])
pipeinfoDF.drop(np.nan,level='Label',inplace=True)
for i in range(4):
    label = "label{}".format(i)
    pipeinfoDF.ix[(label,"A"),"COL1"] = i
    pipeinfoDF.ix[(label,"B"),"COL1"] = 2*i+1

看起来像

COL1
Label  Side      
label0 A        0
       B        1
label1 A        1
       B        3
label2 A        2
       B        5
label3 A        3

我想在label1和label2之间添加一行,以便获得一个新的数据帧

COL1
Label  Side      
label0 A        0
       B        1
label1 A        1
       B        3
oker5  A        10
       B        30
label2 A        2
       B        5
label3 A        3

在我的第一种方法中,我按照上述策略执行此操作

# copy first half to temporary data frame
part1= pipeinfoDF.ix[:"label1"].copy()
# copy second half to temporary data frame
part2= pipeinfoDF.ix["label2":].copy()

# append new row to insert to first part
part1.ix[("oker5","COL1"] = 10
part1.ix[("oker5","COL2"] = 30

# append second half of row to new data frame
for label,side in part2.index:
     print("adding {} {}".format(label,side))
     part1.ix[(label,side),:] = part2.ix[(label,:]

# copy the new data frame to the initial data frame
pipeinfoDF = part1.copy()

事实上,这是有效的;如果我打印出pipeinfoDF,我会得到上面显示的数据框.但是,问题是:如果我想再次执行此操作(因为我想在此初始数据帧中添加多行),我会收到一条错误消息.即使数据帧的另一部分也会导致错误
例如,做

part3= pipeinfoDF2.loc[:"oker5"].copy()

导致错误

Traceback (most recent call last):
  File "C:/Apps/JetBrains/PyCharm Community Edition 4.5.4/jre/jre/bin/DataEelco/.PyCharm/config/scratches/scratch",line 96,in <module>
    part3= pipeinfoDF2.loc[:"oker5"].copy()
  File "C:\Apps\Anaconda\Anaconda2\envs\py34\lib\site-packages\pandas\core\indexing.py",line 1189,in __getitem__
    return self._getitem_axis(key,axis=0)
  File "C:\Apps\Anaconda\Anaconda2\envs\py34\lib\site-packages\pandas\core\indexing.py",line 1304,in _getitem_axis
    return self._get_slice_axis(key,axis=axis)
  File "C:\Apps\Anaconda\Anaconda2\envs\py34\lib\site-packages\pandas\core\indexing.py",line 1211,in _get_slice_axis
    slice_obj.step)
  File "C:\Apps\Anaconda\Anaconda2\envs\py34\lib\site-packages\pandas\core\index.py",line 2340,in slice_indexer
    start_slice,end_slice = self.slice_locs(start,end,step=step,kind=kind)
  File "C:\Apps\Anaconda\Anaconda2\envs\py34\lib\site-packages\pandas\core\index.py",line 4990,in slice_locs
    return super(MultiIndex,self).slice_locs(start,step,line 2490,in slice_locs
    end_slice = self.get_slice_bound(end,'right',kind)
  File "C:\Apps\Anaconda\Anaconda2\envs\py34\lib\site-packages\pandas\core\index.py",line 4961,in get_slice_bound
    return self._partial_tup_index(label,side=side)
  File "C:\Apps\Anaconda\Anaconda2\envs\py34\lib\site-packages\pandas\core\index.py",line 4996,in _partial_tup_index
    (len(tup),self.lexsort_depth))
KeyError: 'Key length (1) was greater than MultiIndex lexsort depth (0)'

我发现这个错误与我想要将数据行添加到已存在的新数据帧这一事实有关

如果我做

print(part1)

我明白了

COL1
Label  Side      
label0 A        0
       B        1
label1 A        1
       B        3

但如果我这样做

print(part1.index)

我知道了

MultiIndex(levels=[['label0','label1','label2','label3'],['A','B']],labels=[[0,1,1],[0,1]],names=['Label','Side'])

换句话说,如果我将行切片到label1,我确实得到一个看起来像这个切片的数据帧,但是多索引级别仍然包含更高级别.显然,如果我试图通过复制part2df的行再次添加这些级别,这将搞乱一切(我不能再切片)

我找到的解决方案有效,但很难看.我没有切片初始数据帧我使用以下例程从空数据帧开始构造part1和part2

def get_slice(dataframe,start_from_label=None,end_at_label=None):
    # create a empty data frame and initialise with rows copy from the dataframe starting from
    # start_from_label and ending at end_at_label
    mi = pd.MultiIndex.from_tuples([tuple([None,names=dataframe.index.names)

    df_new = pd.DataFrame(index=mi,columns=[])
    df_new.drop(np.nan,inplace=True)

    insert = False
    for label,df in dataframe.groupby(level=0):
        side_list = df.index.get_level_values('Side')
        if start_from_label is None or label == start_from_label:
            insert = True
        if insert:
            for side in side_list:
                for col in dataframe.columns:
                    df_new.ix[(label,col] = dataframe.ix[(label,col]

        if end_at_label is not None and label == end_at_label:
            break

    return df_new

在主代码中,在我创建切片的地方,我现在这样做

# part1= pipeinfoDF.ix[:"label1"].copy()
part1 = get_slice(pipeinfoDF,end_at_label="label1")

# part2= pipeinfoDF.ix["label2":].copy()
part2 = get_slice(pipeinfoDF,start_from_label="label2")

所有其余代码保持不变.不同之处在于part1和part2具有干净的多索引字段.如果我打印索引,我得到

MultiIndex(levels=[['label0','label1'],'Side'])

如果我知道再次切片新的数据帧,这可以正常工作:

part3= pipeinfoDF2.loc[:"oker5"].copy()
print(part3)

我明白了

COL1
Label  Side      
label0 A        0
       B        1
label1 A        1
       B        3
oker5  B       30
       A       10

如您所见,结果数据帧上的下一个切片不再引发KeyError,这意味着我可以递归地向此数据帧添加更多行

我的问题现在如下:我如何使用第一个切片方法仍然能够再次切片结果数据框?我发现的解决方案现在基本上通过创建一个空数据框并逐个复制行来实现切片,但我想说它应该可以更好的方式.希望有人能给我一些建议

Eelco

编辑我的评论如下

两个答案都是正确的.事实上,在我的实际情况中,它有点复杂.在该示例中,所有Side标签都是订单A-B,但实际上这可以改变,并且我希望可以自由地施加不同的订单.如果考虑到这一点,第一个使用unstack / stack的anwer不起作用.在拆散之后,我不能再强制执行不同的A / B或B / A.因此,我必须使用dropna选项.

例如,我的数据框架略有不同

for i in range(4):
    label = "label{}".format(i)
    if i!=2:
        l1 = "A"
        l2 = "B"
    else:
        l1 = "B"
        l2 = "A"

    pipeinfoDF.ix[(label,l1),l2),"COL1"] = 2*i+1
    pipeinfoDF.ix[(label,"COL2"] = 10*i
    pipeinfoDF.ix[(label,"COL2"] = 10*(2*i+1)

看起来像

COL1  COL2
Label  Side            
label0 A        0     0
       B        1    10
label1 A        1    10
       B        3    30
label2 B        2    20
       A        5    50
label3 A        3    30
       B        7    70

现在,unstack方法不允许先插入一行,例如先插入B,然后插入A,因为在unstack / stack之后,顺序总是A / B.这不是我想要的.

所以其他解决方案实际上就是我需要的.有一个小问题,也许它也可以解决:-)

我的方法现在是:

# create the lines to add 
newdata = pd.DataFrame(index=pipeinfo_index,columns=[])
newdata.ix[('oker8',"COL1"] = 10
newdata.ix[('oker8',"A"lot,it ),"COL1"] = 30
newdata.ix[('oker8',"COL2"] = 108
newdata.ix[('oker8',"COL2"] = 300
newdata2 = pd.DataFrame(index=pipeinfo_index,columns=[])
newdata2.ix[('oker9',"COL1"] = 20
newdata2.ix[('oker9',"COL1"] = 50
newdata2.ix[('oker9',"COL2"] = 2lot,it 023
newdata2.ix[('oker9',"COL2"] = 5320

#get the indices to add the row
inx1=np.where(pipeinfoDF.reset_index().Label.values=='label1'); inx1=inx1[0][0]+2
inx2=np.where(pipeinfoDF.reset_index().Label.values=='label2'); inx2=inx2[0][0]

#insert the first data row
pipeinfoDF = pd.concat([pipeinfoDF.ix[:inx1],newdata,pipeinfoDF.ix[inx2:]])
pipeinfoDF.drop(np.nan,inplace=True)

这是正确的

COL1  COL2
Label  Side            
label0 A        0     0
       B        1    10
label1 A        1    10
       B        3    30
oker8  B       10   108
       A       30   300
label2 B        2    20
       A        5    50
label3 A        3    30
       B        7    70

并且可以递归地添加第二行

inx1=np.where(pipeinfoDF.reset_index().Label.values=='label2'); inx1=inx1[0][0]+2
inx2=np.where(pipeinfoDF.reset_index().Label.values=='label3'); inx2=inx2[0][0]

pipeinfoDF = pd.concat([pipeinfoDF.ix[:inx1],newdata2,inplace=True)
print(pipeinfoDF)

COL1  COL2
Label  Side            
label0 A        0     0
       B        1    10
label1 A        1    10
       B        3    30
oker8  B       10   108
       A       30   300
label2 B        2    20
       A        5    50
oker9  A       20  2023
       B       50  5320
label3 A        3    30
       B        7    70

这样可行.我唯一不明白的是,一旦开始使用索引切片,使用标签而不是索引进行切片是不可能的.所以,例如,如果你这样做

打印(pipeinfoDF.ix [: ‘LABEL2’])

你再次得到KeyError,而在脚本的开头,在第一个带有索引的切片之前,带有标签的切片工作得很好:你正确得到了

COL1  COL2
Label  Side            
label0 A        0     0
       B        1    10
label1 A        1    10
       B        3    30
label2 B        2    20
       A        5    50

我不确定这是否会在以后给我带来麻烦.但也许有办法解决这个问题呢?好吧,到目前为止:非常感谢两个!

Eelco

解决方法

如果您只想在某个位置插入一组新数据,请尝试以下操作.随着丢弃你收到一个新对象,将不再有KeyError问题.

# create new dataframe based on pipeinfo_index
newdata = pd.DataFrame(index=pipeinfo_index,columns=[])
newdata.ix[('oaker',"COL1"] = 10
newdata.ix[('oaker',"COL1"] = 30

idx = getindexof('label1')

pipeinfoDF = pd.concat([pipeinfoDF.ix[:idx],newdata]) #,pipeinfoDF.ix[idx:]])
# drop the NaN and recieve a new object
pipeinfoDF.drop(np.nan,inplace=True)

pipeinfoDF.index
MultiIndex(levels=[[u'label0',u'label1',u'oaker'],[u'A',u'B']],2,2],names=[u'Label',u'Side'])

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐