在多索引数据中插入一行

Eel*_*iet 5 python insert pandas

我有一个多索引数据框(行上的multindex),我想在某个行号处插入一行。之前已经提出过这个问题(如何在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
Run Code Online (Sandbox Code Playgroud)

看起来像

             COL1
Label  Side      
label0 A        0
       B        1
label1 A        1
       B        3
label2 A        2
       B        5
label3 A        3
Run Code Online (Sandbox Code Playgroud)

我想在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
Run Code Online (Sandbox Code Playgroud)

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

# 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()
Run Code Online (Sandbox Code Playgroud)

实际上,这有效;如果我打印出pipeinfoDF,我将得到上面显示的数据框。但是,这是问题所在:如果我想再次执行此操作(因为我想向此初始数据帧添加多个行),则会收到错误消息。甚至数据帧的进一步切片都会导致错误,例如,

part3= pipeinfoDF2.loc[:"oker5"].copy()
Run Code Online (Sandbox Code Playgroud)

导致错误:

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)'
Run Code Online (Sandbox Code Playgroud)

我发现此错误与以下事实有关:我要将数据行添加到已经存在的新数据框中

如果我做

print(part1)
Run Code Online (Sandbox Code Playgroud)

我懂了

             COL1
Label  Side      
label0 A        0
       B        1
label1 A        1
       B        3
Run Code Online (Sandbox Code Playgroud)

但是如果我这样做

print(part1.index)
Run Code Online (Sandbox Code Playgroud)

我懂了

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

换句话说,如果我将行切片到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
Run Code Online (Sandbox Code Playgroud)

在主代码中,我现在在创建切片的位置

# 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")
Run Code Online (Sandbox Code Playgroud)

其余所有代码保持不变。区别在于part1和part2具有干净的multindex字段。如果我打印索引,我得到

MultiIndex(levels=[['label0', 'label1'], ['A', 'B']],
           labels=[[0, 0, 1, 1], [0, 1, 0, 1]],
           names=['Label', 'Side'])
Run Code Online (Sandbox Code Playgroud)

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

part3= pipeinfoDF2.loc[:"oker5"].copy()
print(part3)
Run Code Online (Sandbox Code Playgroud)

我懂了

             COL1
Label  Side      
label0 A        0
       B        1
label1 A        1
       B        3
oker5  B       30
       A       10
Run Code Online (Sandbox Code Playgroud)

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

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

埃尔科

在下面编辑我的评论

两个答案都是正确的。实际上,在我的实际情况下,情况有点复杂。在示例中,所有“边”标签都是AB顺序,但实际上这可以更改,并且我也想自由地施加不同的顺序。如果考虑到这一点,则带有拆栈/堆叠的第一个应答器将不起作用。取消堆叠后,我不能再施加其他顺序的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)
Run Code Online (Sandbox Code Playgroud)

看起来像

             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
Run Code Online (Sandbox Code Playgroud)

现在,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)
Run Code Online (Sandbox Code Playgroud)

正确地给出

             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
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

给予

             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
Run Code Online (Sandbox Code Playgroud)

所以这可行。我唯一不了解的是,一旦开始使用索引切片,就不可能再使用标签而不是索引进行切片。例如,如果您这样做

打印(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
Run Code Online (Sandbox Code Playgroud)

我不确定这是否会在以后给我带来麻烦。但是也许还有一种方法可以解决此问题?好吧,到目前为止:非常感谢两位答复者!

埃尔科

reb*_*ing 2

如果您只想在某个位置插入一组新数据,请尝试以下操作。通过 drop 你会收到一个新对象,将不再有 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'])
Run Code Online (Sandbox Code Playgroud)