एक पांडा मल्टीआंडेक्स के लिए एक स्तर तैयार करें


105

मेरे पास कुछ ग्रुपिंग के बाद बनाए गए MultiIndex के साथ एक DataFrame है:

import numpy as np
import pandas as p
from numpy.random import randn

df = p.DataFrame({
    'A' : ['a1', 'a1', 'a2', 'a3']
  , 'B' : ['b1', 'b2', 'b3', 'b4']
  , 'Vals' : randn(4)
}).groupby(['A', 'B']).sum()

df

Output>            Vals
Output> A  B           
Output> a1 b1 -1.632460
Output>    b2  0.596027
Output> a2 b3 -0.619130
Output> a3 b4 -0.002009

मैं MultiIndex के लिए एक स्तर कैसे प्रस्तुत करूं ताकि मैं इसे कुछ इस तरह से बदलूं:

Output>                       Vals
Output> FirstLevel A  B           
Output> Foo        a1 b1 -1.632460
Output>               b2  0.596027
Output>            a2 b3 -0.619130
Output>            a3 b4 -0.002009

जवाबों:


139

इसका उपयोग करने का एक अच्छा तरीका एक पंक्ति में है pandas.concat():

import pandas as pd

pd.concat([df], keys=['Foo'], names=['Firstlevel'])

इससे भी छोटा तरीका:

pd.concat({'Foo': df}, names=['Firstlevel'])

यह कई डेटा फ़्रेमों के लिए सामान्यीकृत किया जा सकता है, डॉक्स देखें ।


28
यह स्तंभों के स्तर को जोड़ने के लिए विशेष रूप से अच्छा है axis=1, क्योंकि df.columnsइसमें इंडेक्स की तरह "सेट_इंडेक्स" विधि नहीं है, जो हमेशा मुझे परेशान करता है।
रटगर्स कसीस

2
यह अच्छा है क्योंकि यह pd.Seriesवस्तुओं के लिए भी काम करता है , जबकि वर्तमान में स्वीकृत उत्तर (2013 से) नहीं है।
जॉन

1
अब काम नहीं कर रहा है। TypeError: अस्वाभाविक प्रकार: 'सूची'
cduguet

5
यह मुझे कुछ समय लिया एहसास है कि अगर आप के लिए एक से अधिक कुंजी FirstLevelके रूप में ['Foo', 'Bar'], पहला तर्क भी इसी लंबाई, यानी की आवश्यकता होगी [df] * len(['Foo', 'Bar'])!
mrclng

7
और भी अधिक संक्षिप्त:pd.concat({'Foo': df}, names=['Firstlevel'])
kadee

127

आप पहले इसे एक सामान्य कॉलम के रूप में जोड़ सकते हैं और फिर इसे वर्तमान सूचकांक में जोड़ सकते हैं, इसलिए:

df['Firstlevel'] = 'Foo'
df.set_index('Firstlevel', append=True, inplace=True)

और जरूरत पड़ने पर ऑर्डर बदलें:

df.reorder_levels(['Firstlevel', 'A', 'B'])

जिसके परिणामस्वरूप:

                      Vals
Firstlevel A  B           
Foo        a1 b1  0.871563
              b2  0.494001
           a2 b3 -0.167811
           a3 b4 -1.353409

2
यदि आप मल्टीफ़ंडेक्स कॉलम इंडेक्स के साथ डेटाफ़्रेम के साथ ऐसा करते हैं, तो यह उन स्तरों को जोड़ता है, जो शायद ज्यादातर मामलों में मायने नहीं रखते हैं, लेकिन हो सकता है, अगर आप मेटाडेटा पर कुछ और भरोसा कर रहे हों।
n

22

मुझे लगता है कि यह एक अधिक सामान्य समाधान है:

# Convert index to dataframe
old_idx = df.index.to_frame()

# Insert new level at specified location
old_idx.insert(0, 'new_level_name', new_level_values)

# Convert back to MultiIndex
df.index = pandas.MultiIndex.from_frame(old_idx)

अन्य उत्तरों के कुछ फायदे:

  • नया स्तर किसी भी स्थान पर जोड़ा जा सकता है, न कि केवल शीर्ष पर।
  • यह विशुद्ध रूप से सूचकांक पर एक हेरफेर है और डेटा की हेरफेर करने की आवश्यकता नहीं है, जैसे कि समवर्ती चाल।
  • इसमें एक मध्यवर्ती चरण के रूप में एक कॉलम जोड़ने की आवश्यकता नहीं होती है, जो बहु-स्तरीय कॉलम इंडेक्स को तोड़ सकता है।

2

मैंने cxrodgers उत्तर से थोड़ा सा कार्य किया , जो IMHO सबसे अच्छा समाधान है क्योंकि यह शुद्ध रूप से किसी भी डेटा फ्रेम या श्रृंखला से स्वतंत्र सूचकांक पर काम करता है।

मेरे द्वारा जोड़ा गया एक फिक्स है: to_frame()विधि इंडेक्स स्तरों के लिए नए नामों का आविष्कार करेगी जिनके पास एक नहीं है। जैसे कि नए सूचकांक में ऐसे नाम होंगे जो पुराने सूचकांक में मौजूद नहीं हैं। इस नाम-परिवर्तन को वापस करने के लिए मैंने कुछ कोड जोड़े।

नीचे कोड है, मैंने इसे कुछ समय के लिए उपयोग किया है और यह ठीक काम करने लगता है। यदि आपको कोई समस्या या किनारे के मामले मिलते हैं, तो मैं अपने उत्तर को समायोजित करने के लिए बहुत बाध्य हूं।

import pandas as pd

def _handle_insert_loc(loc: int, n: int) -> int:
    """
    Computes the insert index from the right if loc is negative for a given size of n.
    """
    return n + loc + 1 if loc < 0 else loc


def add_index_level(old_index: pd.Index, value: Any, name: str = None, loc: int = 0) -> pd.MultiIndex:
    """
    Expand a (multi)index by adding a level to it.

    :param old_index: The index to expand
    :param name: The name of the new index level
    :param value: Scalar or list-like, the values of the new index level
    :param loc: Where to insert the level in the index, 0 is at the front, negative values count back from the rear end
    :return: A new multi-index with the new level added
    """
    loc = _handle_insert_loc(loc, len(old_index.names))
    old_index_df = old_index.to_frame()
    old_index_df.insert(loc, name, value)
    new_index_names = list(old_index.names)  # sometimes new index level names are invented when converting to a df,
    new_index_names.insert(loc, name)        # here the original names are reconstructed
    new_index = pd.MultiIndex.from_frame(old_index_df, names=new_index_names)
    return new_index

यह निम्नलिखित unittest कोड पारित किया:

import unittest

import numpy as np
import pandas as pd

class TestPandaStuff(unittest.TestCase):

    def test_add_index_level(self):
        df = pd.DataFrame(data=np.random.normal(size=(6, 3)))
        i1 = add_index_level(df.index, "foo")

        # it does not invent new index names where there are missing
        self.assertEqual([None, None], i1.names)

        # the new level values are added
        self.assertTrue(np.all(i1.get_level_values(0) == "foo"))
        self.assertTrue(np.all(i1.get_level_values(1) == df.index))

        # it does not invent new index names where there are missing
        i2 = add_index_level(i1, ["x", "y"]*3, name="xy", loc=2)
        i3 = add_index_level(i2, ["a", "b", "c"]*2, name="abc", loc=-1)
        self.assertEqual([None, None, "xy", "abc"], i3.names)

        # the new level values are added
        self.assertTrue(np.all(i3.get_level_values(0) == "foo"))
        self.assertTrue(np.all(i3.get_level_values(1) == df.index))
        self.assertTrue(np.all(i3.get_level_values(2) == ["x", "y"]*3))
        self.assertTrue(np.all(i3.get_level_values(3) == ["a", "b", "c"]*2))

        # df.index = i3
        # print()
        # print(df)

0

पंडों के साथ खरोंच से इसे बनाने के बारे में कैसे। मल्तिइंडेक्स.फ्रॉम_टुपल्स ?

df.index = p.MultiIndex.from_tuples(
    [(nl, A, B) for nl, (A, B) in
        zip(['Foo'] * len(df), df.index)],
    names=['FirstLevel', 'A', 'B'])

इसी तरह cxrodger के समाधान के लिए , यह एक लचीला तरीका है और डेटाफ़्रेम के लिए अंतर्निहित सरणी को संशोधित करने से बचा जाता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.