Python 在多索引数据中插入一行

Python 在多索引数据中插入一行,python,pandas,insert,Python,Pandas,Insert,我有一个多索引数据帧(行上的多索引),我想在某个行号插入一行。这个问题以前被问过,但我有一个不同的问题要解决 事实上。我解决了它,但它是如此丑陋,我想问,是否有更好的方法来做到这一点 由于pandas中没有insert_row函数,所以我想先使用切片将数据帧复制到某一行,再将要插入的行追加到新帧,然后追加原始数据帧的其余部分,从而插入一行。事实上,这种方法是有效的。这里有一个例子 首先,我用 pipeinfo_index = pd.MultiIndex.from_tuples([tuple([N

我有一个多索引数据帧(行上的多索引),我想在某个行号插入一行。这个问题以前被问过,但我有一个不同的问题要解决

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

由于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", "B"),"COL1"] = 10
part1.ix[("oker5", "A"),"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, side),:]

# 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, end, step, kind=kind)
  File "C:\Apps\Anaconda\Anaconda2\envs\py34\lib\site-packages\pandas\core\index.py", 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)'
我明白了

             COL1
Label  Side      
label0 A        0
       B        1
label1 A        1
       B        3
             COL1
Label  Side      
label0 A        0
       B        1
label1 A        1
       B        3
oker5  B       30
       A       10
但如果我这样做了

print(part1.index)
我明白了

换句话说,如果我将行切片到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, None])], names=dataframe.index.names)

    df_new = pd.DataFrame(index=mi, columns=[])
    df_new.drop(np.nan, level='Label', 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, side),col] = dataframe.ix[(label, side),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有一个干净的multindex字段。如果我打印索引,我会得到

MultiIndex(levels=[['label0', 'label1'], ['A', 'B']],
           labels=[[0, 0, 1, 1], [0, 1, 0, 1]],
           names=['Label', 'Side'])
如果我知道再次对新数据帧进行切片,则此操作正常:

part3= pipeinfoDF2.loc[:"oker5"].copy()
print(part3)
我明白了

             COL1
Label  Side      
label0 A        0
       B        1
label1 A        1
       B        3
             COL1
Label  Side      
label0 A        0
       B        1
label1 A        1
       B        3
oker5  B       30
       A       10
如您所见,结果数据帧上的下一个切片不再引发KeyError,这意味着我可以递归地向该数据帧添加更多行

我现在的问题是:如何使用第一个切片方法,并且仍然能够再次切片生成的数据帧?我现在发现的解决方案基本上是通过创建一个空数据帧并逐个复制行来实现切片,但我认为这应该是一种更好的方式。希望有人能给我一些建议

Eelco

编辑下面我的评论

两个答案都是正确的。事实上,在我的真实情况下,情况有点复杂。在这个例子中,所有的侧标签都是A-B顺序,但实际上这可能会改变,而且我希望有自由施加不同的顺序。如果考虑到这一点,第一个取消堆叠/堆叠的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),"COL1"] = i
    pipeinfoDF.ix[(label, l2),"COL1"] = 2*i+1
    pipeinfoDF.ix[(label, l1),"COL2"] = 10*i
    pipeinfoDF.ix[(label, l2),"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', "B"), "COL1"] = 10
newdata.ix[('oker8', "A"lot, it ), "COL1"] = 30
newdata.ix[('oker8', "B"), "COL2"] = 108
newdata.ix[('oker8', "A"), "COL2"] = 300
newdata2 = pd.DataFrame(index=pipeinfo_index, columns=[])
newdata2.ix[('oker9', "A"), "COL1"] = 20
newdata2.ix[('oker9', "B"), "COL1"] = 50
newdata2.ix[('oker9', "A"), "COL2"] = 2lot, it 023
newdata2.ix[('oker9', "B"), "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, level='Label', 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, pipeinfoDF.ix[inx2:]])
pipeinfoDF.drop(np.nan, level='Label', 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', "A"), "COL1"] = 10
newdata.ix[('oaker', "B"), "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, level='Label', inplace=True)

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