2015-09-07 4 views
4

У меня есть мультииндексный фреймворк (мультииндекс на строках), и я хочу вставить строку с определенным номером строки. Этот вопрос был рассмотрен раньше (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 и Метка2 таким образом, чтобы получить новый dataframe например

   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)' 

Я обнаружил, что эта ошибка связана с тем, что я хочу добавить строки данных в новый кадр данных, которые уже существуют

Если я

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, 0, 1, 1], [0, 1, 0, 1]], 
      names=['Label', 'Side']) 

Другими словами, если я нарежьте строки до LABEL1 я действительно получить dataframe, который выглядит как этот срез, но уровни multiindex по-прежнему содержат более высокие уровни. По-видимому, если я попытаюсь добавить этот уровень позже, скопировав строки 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") 

Остальная часть кода остается неизменной. Разница в том, что в частях 1 и 2 есть чистое поле с несколькими индексами. Если я печатаю индекс я получаю

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

Если я знаю, нарезать новую dataframe снова, это работает отлично:

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, что означает, что я могу рекурсивно добавить в этот файл больше строк.

Теперь мой вопрос следующий: как я могу использовать первый метод slicing и все еще иметь возможность разрезать результирующий фрейм данных снова? Решение, которое я нашел сейчас, в основном реализует срез, создавая пустой кадр данных и копируя строки один за другим, но я бы сказал, что это должно быть возможно в лучшем виде. Надеюсь, кто-нибудь может дать мне несколько советов

Eelco

Редактировать на мой комментарий ниже

Оба ответа верны. Фактически, в моей реальной ситуации это было немного сложнее. В примере все метки Side - это A-B, но на самом деле это может измениться, а также я хочу иметь свободу навязывать другой порядок. Если вы учтете это, первый андер с помощью стека/стека не работает. После взлома я больше не могу навязывать другой A/B или B/A. Поэтому я должен использовать опцию с dropna.

Пример, мой dataframe немного отличается

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 

Теперь разборки стопки метод не позволяет вставить строку с, например, B, а затем А, потому после unack/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

ответ

0

Если вы хотите просто вставить новый набор данных в определенную позицию, попробуйте следующее. Когда вы получите 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']) 
0

Я думаю, что лучше работать с Index вместо Multiindex. Поэтому сначала я откладываю df, создаю новые dfs, а затем объединяю их. В последний раз я ставил df обратно.

import pandas as pd 
import numpy as np 

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 

print pipeinfoDF 
# 
#    COL1 
#Label Side 
#label0 A  0 
#  B  1 
#label1 A  1 
#  B  3 
#label2 A  2 
#  B  5 
#label3 A  3 
#  B  7 

#pivot df - Multiindex convert to Index 
pipeinfoDF = pipeinfoDF.unstack() 

#create df for insert - one row in 1. df or 2 row in 2. df 
line = pd.DataFrame([[10, 30]], index=['oker5'], columns=pipeinfoDF.columns) 
line1 = pd.DataFrame([[20, 50], [40,60]], index=['hron','ipel'], columns=pipeinfoDF.columns) 

#concat all df 
pipeinfoDF = pd.concat([pipeinfoDF.ix[:'label1'], line, line1, pipeinfoDF.ix['label2':]]) 

#stack data back for Multiindex 
pipeinfoDF = pipeinfoDF.stack() 

print pipeinfoDF 
# 
#    COL1 
#Label Side 
#label0 A  0 
#  B  1 
#label1 A  1 
#  B  3 
#oker5 A  10 
#  B  30 
#hron A  20 
#  B  50 
#ipel A  40 
#  B  60 
#label2 A  2 
#  B  5 
#label3 A  3 
#  B  7 
+0

Спасибо за ваш ответ. Он работает, если метка A/B не изменяется в порядке, но я немного изменил свой вопрос, потому что в моем реальном коде мне нужно иметь возможность изменять A/B в B/A. Я не думаю, что в этом случае вы все еще можете использовать метод stack –

Смежные вопросы