web-dev-qa-db-ja.com

Pandas集計関数で返される列に名前を付けますか?

Pandasのgroupby機能に問題があります。私は ドキュメント を読みましたが、集約関数を複数の列に適用する方法がわかりませんandそれらの列にはカスタム名があります。

これは非常に近いものですが、返されるデータ構造には列見出しがネストされています。

data.groupby("Country").agg(
        {"column1": {"foo": sum()}, "column2": {"mean": np.mean, "std": np.std}})

(つまり、column2の平均と標準を取りたいが、それらの列を「平均」と「標準」として返す)

私は何が欠けていますか?

63
David Chouinard

これにより、階層列インデックスから最も外側のレベルが削除されます。

df = data.groupby(...).agg(...)
df.columns = df.columns.droplevel(0)

最も外側のレベルを維持したい場合は、複数レベルの列でravel()関数を使用して新しいラベルを作成できます。

df.columns = ["_".join(x) for x in df.columns.ravel()]

例えば:

import pandas as pd
import pandas.rpy.common as com
import numpy as np

data = com.load_data('Loblolly')
print(data.head())
#     height  age Seed
# 1     4.51    3  301
# 15   10.89    5  301
# 29   28.72   10  301
# 43   41.74   15  301
# 57   52.70   20  301

df = data.groupby('Seed').agg(
    {'age':['sum'],
     'height':['mean', 'std']})
print(df.head())
#       age     height           
#       sum        std       mean
# Seed                           
# 301    78  22.638417  33.246667
# 303    78  23.499706  34.106667
# 305    78  23.927090  35.115000
# 307    78  22.222266  31.328333
# 309    78  23.132574  33.781667

df.columns = df.columns.droplevel(0)
print(df.head())

利回り

      sum        std       mean
Seed                           
301    78  22.638417  33.246667
303    78  23.499706  34.106667
305    78  23.927090  35.115000
307    78  22.222266  31.328333
309    78  23.132574  33.781667

または、インデックスの最初のレベルを保持するには:

df = data.groupby('Seed').agg(
    {'age':['sum'],
     'height':['mean', 'std']})
df.columns = ["_".join(x) for x in df.columns.ravel()]

利回り

      age_sum   height_std  height_mean
Seed                           
301        78    22.638417    33.246667
303        78    23.499706    34.106667
305        78    23.927090    35.115000
307        78    22.222266    31.328333
309        78    23.132574    33.781667
82
unutbu

pandas> = 0.25

返された集約列に名前を付ける機能は masterブランチで再導入されました で、pandas 0.25を対象としています。新しい構文は.agg(new_col_name=('col_name', 'agg_func')です。上記リンクのPRの例:

_In [2]: df = pd.DataFrame({'kind': ['cat', 'dog', 'cat', 'dog'],
   ...:                    'height': [9.1, 6.0, 9.5, 34.0],
   ...:                    'weight': [7.9, 7.5, 9.9, 198.0]})
   ...:

In [3]: df
Out[3]:
  kind  height  weight
0  cat     9.1     7.9
1  dog     6.0     7.5
2  cat     9.5     9.9
3  dog    34.0   198.0

In [4]: df.groupby('kind').agg(min_height=('height', 'min'), 
                               max_weight=('weight', 'max'))
Out[4]:
      min_height  max_weight
kind
cat          9.1         9.9
dog          6.0       198.0
_

this PR のように、この構文と以前に(以下で)提案した2段階の名前変更構文で複数のラムダ式を使用することも可能です。繰り返しますが、PRの例からコピーします。

_In [2]: df = pd.DataFrame({"A": ['a', 'a'], 'B': [1, 2], 'C': [3, 4]})

In [3]: df.groupby("A").agg({'B': [lambda x: 0, lambda x: 1]})
Out[3]:
         B
  <lambda> <lambda 1>
A
a        0          1
_

そして.rename()、または一度に:

_In [4]: df.groupby("A").agg(b=('B', lambda x: 0), c=('B', lambda x: 1))
Out[4]:
   b  c
A
a  0  0
_

pandas <0.25

Unutbuによる現在受け入れられている回答は、これをpandasバージョン<= 0.20で行う素晴らしい方法です。ただし、pandas 0.20では、このメソッドを使用すると、パンダの将来のバージョンでは構文が使用できなくなることを示す警告。

シリーズ:

FutureWarning:集計にSeriesでdictを使用することは非推奨であり、将来のバージョンでは削除されます

データフレーム:

FutureWarning:名前を変更して辞書を使用することは非推奨であり、将来のバージョンでは削除されます

pandas 0.20 changelog によると、集計中に列の名前を変更する推奨方法は次のとおりです。

_# Create a sample data frame
df = pd.DataFrame({'A': [1, 1, 1, 2, 2],
                   'B': range(5),
                   'C': range(5)})

# ==== SINGLE COLUMN (SERIES) ====
# Syntax soon to be deprecated
df.groupby('A').B.agg({'foo': 'count'})
# Recommended replacement syntax
df.groupby('A').B.agg(['count']).rename(columns={'count': 'foo'})

# ==== MULTI COLUMN ====
# Syntax soon to be deprecated
df.groupby('A').agg({'B': {'foo': 'sum'}, 'C': {'bar': 'min'}})
# Recommended replacement syntax
df.groupby('A').agg({'B': 'sum', 'C': 'min'}).rename(columns={'B': 'foo', 'C': 'bar'})
# As the recommended syntax is more verbose, parentheses can
# be used to introduce line breaks and increase readability
(df.groupby('A')
    .agg({'B': 'sum', 'C': 'min'})
    .rename(columns={'B': 'foo', 'C': 'bar'})
)
_

詳細については、 .20 changelog をご覧ください。

@JunkMechanicのコメントに応じて2017-01-03を更新します。

古いスタイルの辞書構文では、複数のlambda関数を_.agg_に渡すことができました。これらの関数は、渡された辞書のキーで名前が変更されるためです。

_>>> df.groupby('A').agg({'B': {'min': lambda x: x.min(), 'max': lambda x: x.max()}})

    B    
  max min
A        
1   2   0
2   4   3
_

複数の関数をリストとして単一の列に渡すこともできます。

_>>> df.groupby('A').agg({'B': [np.min, np.max]})

     B     
  amin amax
A          
1    0    2
2    3    4
_

ただし、ラムダ関数は匿名であり、すべてが_<lambda>_を返すため、ラムダ関数では機能しません。これにより、名前の競合が発生します。

_>>> df.groupby('A').agg({'B': [lambda x: x.min(), lambda x: x.max]})
SpecificationError: Function names must be unique, found multiple named <lambda>
_

SpecificationErrorを回避するために、lambdaを使用する代わりに、名前付き関数を事前に定義できます。適切な関数名は、後でデータフレームで_.rename_を呼び出すことも避けます。これらの関数は、上記と同じリスト構文で渡すことができます。

_>>> def my_min(x):
>>>     return x.min()

>>> def my_max(x):
>>>     return x.max()

>>> df.groupby('A').agg({'B': [my_min, my_max]})

       B       
  my_min my_max
A              
1      0      2
2      3      4
_
53
joelostblom

JMPに似た動作をしたい場合は、使用できるマルチインデックスからのすべての情報を保持する列タイトルを作成します。

newidx = []
for (n1,n2) in df.columns.ravel():
    newidx.append("%s-%s" % (n1,n2))
df.columns=newidx

データフレームを次から変更します:

    I                       V
    mean        std         first
V
4200.0  25.499536   31.557133   4200.0
4300.0  25.605662   31.678046   4300.0
4400.0  26.679005   32.919996   4400.0
4500.0  26.786458   32.811633   4500.0

    I-mean      I-std       V-first
V
4200.0  25.499536   31.557133   4200.0
4300.0  25.605662   31.678046   4300.0
4400.0  26.679005   32.919996   4400.0
4500.0  26.786458   32.811633   4500.0
6
Gadi Oron

私は、同じ場所で出力列に名前を付けて定義する方が自然で一貫しているように思えるOPに同意します(例: tidyverse's summarize in R )、しかし作業-around in pandas今のところは、 assignbefore集約を行う:

data.assign(
    f=data['column1'],
    mean=data['column2'],
    std=data['column2']
).groupby('Country').agg(dict(f=sum, mean=np.mean, std=np.std)).reset_index()

reset_index 回る'Country''f''mean'、および'std'すべてを個別の整数インデックスを持つ通常の列に入れます。)

4
user3780389

この種のデータフレームなど、列名には2つのレベルがあります。

 shop_id  item_id   date_block_num item_cnt_day       
                                  target              
0   0       30          1            31               

このコードを使用できます:

df.columns = [col[0] if col[-1]=='' else col[-1] for col in df.columns.values]

結果は次のとおりです。

 shop_id  item_id   date_block_num target              
0   0       30          1            31 
0
saneryee

@Joel Ostblomのインスピレーションで

単に集約用の実行可能なディクショナリを既にお持ちの場合は、新しいバージョンの集約用に次のコードを使用/変更し、集約と名前の変更部分を分離できます。複数のアイテムがある場合は、ネストされた辞書に注意してください。

def agg_translate_agg_rename(input_agg_dict):
    agg_dict = {}
    rename_dict = {}
    for k, v in input_agg_dict.items():
        if len(v) == 1:
            agg_dict[k] = list(v.values())[0]
            rename_dict[k] = list(v.keys())[0]
        else:
            updated_index = 1
            for nested_dict_k, nested_dict_v in v.items():
                modified_key = k + "_" + str(updated_index)
                agg_dict[modified_key] = nested_dict_v
                rename_dict[modified_key] = nested_dict_k
                updated_index += 1
    return agg_dict, rename_dict

one_dict = {"column1": {"foo": 'sum'}, "column2": {"mean": 'mean', "std": 'std'}}
agg, rename = agg_translator_aa(one_dict)

我々が得る

agg = {'column1': 'sum', 'column2_1': 'mean', 'column2_2': 'std'}
rename = {'column1': 'foo', 'column2_1': 'mean', 'column2_2': 'std'}

もっと賢い方法があれば教えてください。ありがとう。

0
udothemath1984