获取多索引中级别的最后一个元素

时间:2016-06-19 07:46:35

标签: python numpy pandas dataframe multi-index

我有这种格式的数据框:

a   b   x
1   1   31
1   2   1
1   3   42
1   4   423
1   5   42
1   6   3
1   7   44
1   8   65437
1   9   73
2   1   5656
2   2   7
2   3   5
2   4   5
2   5   34

ab是索引,x是值。

我希望获得行1 9 732 5 34,换句话说,就是该级别的最后一行。

我一直在弄.loc.iloc.xs一小时,但我无法让它发挥作用。我该怎么做?

3 个答案:

答案 0 :(得分:3)

您可以groupby使用last

print (df.groupby('a', as_index=False).last())
   a  b   x
0  1  9  73
1  2  5  34

如果ab的级别为MultiIndex,请先致电reset_index

print (df.reset_index().groupby('a', as_index=False).last())
   a  b   x
0  1  9  73
1  2  5  34

答案 1 :(得分:2)

#include <stdlib.h> #include <stdio.h> struct Product//declaring a structure { char code[25]; //variables inside a structure char name[25]; double price; int quantity; }; int main(void) { struct Product product; int add; FILE *fptr; FILE *nfptr; printf("-------------------------------\n"); printf(" ADD ITEM\n"); printf("-------------------------------\n"); printf("1.GST Items\n"); printf("2.Non-GST Items\n"); scanf("%d", &add); switch (add) //start of switch statement { case 1: fptr = fopen("gst.txt", "wr"); //open file if (fptr == NULL)//checking whether the file is empty or not { printf("File cannot be found\n"); } else //else statement { printf("Add Item Code:\n"); scanf("%s", product.code); printf("Item name:\n"); scanf("%s", product.name); printf("Item price:\n"); scanf("%.2lf", &product.price); printf("Item quantity:\n"); scanf("%d", &product.quantity); fprintf(fptr, "%s;%s;%.2f;%d\n", product.code, product.name, product.price, product.quantity); fclose(fptr); }//end else statement break; case 2: nfptr = fopen("ngst.txt", "r"); //open file if (nfptr == NULL)//checking whether the file is empty or not { printf("File cannot be found\n"); } else //else statement { printf("Add Item Code:\n"); scanf("%s", product.code); printf("Item name:\n"); scanf("%s", product.name); printf("Item price:\n"); scanf("%.2lf", &product.price); printf("Item quantity:\n"); scanf("%d", &product.quantity); fprintf(nfptr, "%s;%s;%.2f;%d\n", product.code, product.name, product.price, product.quantity); fclose(nfptr); }//end else statement break; } } 作为数据框并且列df已经排序,这是一种方法 -

a

基本思想是我们沿着排序列df[np.append(np.diff(df['a'])>0,True)] 执行区分,并使用a查找正变化,为我们提供一个布尔数组。布尔数组中的(>0)元素将表示该列中“组”的结尾。因为,最后一个组的最后一个元素没有变化,所以我们需要在末尾附加一个true元素到该布尔数组。最后,使用这样一个布尔数组索引True以从中选择行并为我们提供所需的输出。

可以使用np.unique使用其可选参数df建议另一种方法,该方法将为我们提供每个组的第一个出现元素的索引。因此,要使其适用于最后一个元素,只需翻转列return_index,使用a并获取第一个出现的索引,然后从np.unique中的总行数中减去它们。最后,使用最终输出的索引到df。因此,实施将是 -

df

示例运行 -

df.iloc[df.shape[0] - np.unique(df['a'][::-1],return_index=True)[1] - 1]

答案 2 :(得分:1)

特殊情况

jezrael提出的groupby解决方案是高级的通用解决方案。但是,当groupby生成许多不同的组时(在OP给出的示例中,这可能是由a的许多不同值引起的),它的性能非常差。在这里,我为特殊情况(与OP的情况相匹配)提出了一种优化的解决方案。

假设您有一个由MultiIndex索引的数据帧,该数据帧具有多个级别,并且这些级别的 last 的值始终在每个组中以相同的值开始;例如,假设值始终从1开始并递增。在下面的示例中,这将是number级别。

                value
name number          
a    1       0.548126
b    1       0.774775
     2       0.483701
     3       0.820758
c    1       0.696832
     2       0.905071
d    1       0.750546
     2       0.761081
e    1       0.944682
     2       0.336210

然后,要获得number的每个唯一值(或具有其他任何级别的值的组合)的最大/最后一个name值的行的横截面,可以执行以下操作:

df[np.roll(df.index.get_level_values('number') == 1, -1)]

您会得到:

                value
name number          
a    1       0.548126
b    3       0.820758
c    2       0.905071
d    2       0.761081
e    2       0.336210

说明

一个接一个:

  • df.index.get_level_values('number'):获取每一行number级别的值的数组
  • df.index.get_level_values('number') == 1:对于True为1的那些行,布尔数组为number
  • np.roll(df.index.get_level_values('number') == 1, -1):以循环方式将前一个数组的所有值向后移一个位置(即第一个元素变为最后一个,第二个元素变为第一个,依此类推)。

想法是,组的 last 值始终紧接在组的 first 值之前,该值始终为1。因此,如果我们为number值为1的行获得布尔掩码,则可以将所有这些布尔 backwards 移位一个,最后得到一个掩码值number

考虑到最后一行的特殊情况,可以通过循环移位 来进行考虑,这样第一个布尔值最终会落在最后—第一行的number始终等于{{1} },因此布尔值将始终为1,因此总是(如预期的那样)选择最后一行。

泛型函数

True

要玩的设置代码

def innermost_level_max(df, start_value=1, drop_level=False):
    assert df.index.is_lexsorted()
    level_values = df.index.get_level_values(-1)
    result = df[np.roll(level_values == start_value, -1)]
    if drop_level:
        result = result.droplevel(-1)
    return result

这允许您创建一个示例数据框:

import itertools as itt

import numpy as np
import pandas as pd
import perfplot

rng = np.random.default_rng(42)


def generate_names():
    alphabet = [chr(i) for i in range(ord('a'), ord('z') + 1)]
    for length in itt.count(1):
        for tup in itt.product(*([alphabet]*length)):
            yield ''.join(tup)


def make_ragged_df(n):
    lengths = rng.integers(1, 3, endpoint=True, size=n)
    names = np.fromiter(
        itt.chain.from_iterable(itt.repeat(n, times=r) for n, r in zip(generate_names(), lengths)),
        dtype='U100',
        count=n
    )
    numbers = np.fromiter(itt.chain.from_iterable(map(range, lengths)), int, count=n) + 1
    index = pd.MultiIndex.from_arrays([names, numbers], names=['name', 'number'])
    data = np.random.rand(n)
    df = pd.DataFrame({'value': data}, index=index)
    return df

性能

使用>>> make_ragged_df(10) value name number a 1 0.548126 b 1 0.774775 2 0.483701 3 0.820758 c 1 0.696832 2 0.905071 d 1 0.750546 2 0.761081 e 1 0.944682 2 0.336210

perfplot

performance plot


更特殊的情况

如果您知道import perfplot benchmarks = perfplot.bench( setup=lambda n: make_ragged_df(n), kernels=[ lambda df: df.groupby('name', sort=False).tail(1), lambda df: df[np.roll(df.index.get_level_values('number') == 1, -1)], ], labels=["with groupby", "with np.roll on == 1"], n_range=range(50, 10000, 500), xlabel="total number of rows", ) benchmarks.show() 始终的最后一个值是什么,例如3,您只需要索引切片就可以了:

number

或横截面:

df.loc[pd.IndexSlice[:, 3], :]

但是如果是这种情况,那么您一开始就不会读这个问题。