GroupBy पंडों DataFrame और सबसे आम मूल्य का चयन करें


99

मेरे पास तीन स्ट्रिंग कॉलम के साथ एक डेटा फ्रेम है। मुझे पता है कि तीसरे कॉलम में केवल एक मान पहले दो के हर संयोजन के लिए मान्य है। पहले दो स्तंभों द्वारा डेटा फ्रेम द्वारा मुझे समूह को साफ करने के लिए और प्रत्येक संयोजन के लिए तीसरे स्तंभ के सबसे सामान्य मूल्य का चयन करना होगा।

मेरा कोड:

import pandas as pd
from scipy import stats

source = pd.DataFrame({'Country' : ['USA', 'USA', 'Russia','USA'], 
                  'City' : ['New-York', 'New-York', 'Sankt-Petersburg', 'New-York'],
                  'Short name' : ['NY','New','Spb','NY']})

print source.groupby(['Country','City']).agg(lambda x: stats.mode(x['Short name'])[0])

अंतिम पंक्ति का कोड काम नहीं करता है, यह "कुंजी त्रुटि 'संक्षिप्त नाम' कहता है और अगर मैं केवल शहर द्वारा समूह बनाने की कोशिश करता हूं, तो मुझे एक जोर मिला। मैं इसे क्या ठीक कर सकता हूं?

जवाबों:


145

आप value_counts()एक गणना श्रृंखला प्राप्त करने के लिए उपयोग कर सकते हैं , और पहली पंक्ति प्राप्त कर सकते हैं :

import pandas as pd

source = pd.DataFrame({'Country' : ['USA', 'USA', 'Russia','USA'], 
                  'City' : ['New-York', 'New-York', 'Sankt-Petersburg', 'New-York'],
                  'Short name' : ['NY','New','Spb','NY']})

source.groupby(['Country','City']).agg(lambda x:x.value_counts().index[0])

मामले में आप .gg () में अन्य एग फ़ंक्शन करने के बारे में सोच रहे हैं।

# Let's add a new col,  account
source['account'] = [1,2,3,3]

source.groupby(['Country','City']).agg(mod  = ('Short name', \
                                        lambda x: x.value_counts().index[0]),
                                        avg = ('account', 'mean') \
                                      )

मैंने पाया है कि स्ट्रिंग वेरिएबल्स के मामले में आँकड़ों के लिए गलत उत्तर दिखा सकते हैं। यह रास्ता अधिक विश्वसनीय लगता है।
वायाचेस्लाव नेफेडोव

1
क्या यह नहीं होना चाहिए .value_counts(ascending=False)?
प्राइवेट

1
@ मुख्य: ascending=Falseपहले से ही डिफ़ॉल्ट मान है, इसलिए स्पष्ट रूप से आदेश सेट करने की आवश्यकता नहीं है।
शमूड़ी

2
जैसा कि जैक्वॉट ने कहा, pd.Series.modeअब अधिक उपयुक्त और तेज है।
Daisuke SHIBATO

मैं इस समाधान का उपयोग कई अलग-अलग एकत्रीकरण कार्यों के साथ कैसे कर सकता हूं, उदाहरण के लिए यदि मेरे पास "संक्षिप्त नाम" जैसे कई कॉलम हैं और इसके अतिरिक्त संख्यात्मक कॉलम हैं जो मुझे एक राशि फ़ंक्शन के साथ एकत्र करना चाहते हैं?
constiii

99

पंडों> = 0.16

pd.Series.mode उपलब्ध है!

फ़ंक्शन का उपयोग करें groupby, GroupBy.aggऔर pd.Series.modeप्रत्येक समूह में फ़ंक्शन लागू करें :

source.groupby(['Country','City'])['Short name'].agg(pd.Series.mode)

Country  City            
Russia   Sankt-Petersburg    Spb
USA      New-York             NY
Name: Short name, dtype: object

यदि यह DataFrame के रूप में आवश्यक है, तो उपयोग करें

source.groupby(['Country','City'])['Short name'].agg(pd.Series.mode).to_frame()

                         Short name
Country City                       
Russia  Sankt-Petersburg        Spb
USA     New-York                 NY

इसके बारे Series.modeमें उपयोगी बात यह है कि यह हमेशा एक श्रृंखला लौटाता है, जिससे यह बहुत संगत हो जाता है aggऔर applyविशेष रूप से जब ग्रुपबी आउटपुट का पुनर्निर्माण करता है। यह तेज भी है।

# Accepted answer.
%timeit source.groupby(['Country','City']).agg(lambda x:x.value_counts().index[0])
# Proposed in this post.
%timeit source.groupby(['Country','City'])['Short name'].agg(pd.Series.mode)

5.56 ms ± 343 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.76 ms ± 387 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

एकाधिक मोड से निपटना

Series.modeकई मोड होने पर भी अच्छा काम करता है :

source2 = source.append(
    pd.Series({'Country': 'USA', 'City': 'New-York', 'Short name': 'New'}),
    ignore_index=True)

# Now `source2` has two modes for the 
# ("USA", "New-York") group, they are "NY" and "New".
source2

  Country              City Short name
0     USA          New-York         NY
1     USA          New-York        New
2  Russia  Sankt-Petersburg        Spb
3     USA          New-York         NY
4     USA          New-York        New

source2.groupby(['Country','City'])['Short name'].agg(pd.Series.mode)

Country  City            
Russia   Sankt-Petersburg          Spb
USA      New-York            [NY, New]
Name: Short name, dtype: object

या, यदि आप प्रत्येक मोड के लिए एक अलग पंक्ति चाहते हैं, तो आप उपयोग कर सकते हैं GroupBy.apply:

source2.groupby(['Country','City'])['Short name'].apply(pd.Series.mode)

Country  City               
Russia   Sankt-Petersburg  0    Spb
USA      New-York          0     NY
                           1    New
Name: Short name, dtype: object

यदि आप परवाह नहीं करते हैं कि कौन सा मोड तब तक वापस आ जाता है जब तक कि यह उनमें से एक है, तो आपको एक लैम्ब्डा की आवश्यकता होगी जो कॉल करता है modeऔर पहला परिणाम निकालता है।

source2.groupby(['Country','City'])['Short name'].agg(
    lambda x: pd.Series.mode(x)[0])

Country  City            
Russia   Sankt-Petersburg    Spb
USA      New-York             NY
Name: Short name, dtype: object

(नहीं) के विकल्प पर विचार करें

आप statistics.modeअजगर से भी उपयोग कर सकते हैं , लेकिन ...

source.groupby(['Country','City'])['Short name'].apply(statistics.mode)

Country  City            
Russia   Sankt-Petersburg    Spb
USA      New-York             NY
Name: Short name, dtype: object

... कई मोड से निपटने के लिए यह अच्छी तरह से काम नहीं करता है; a StatisticsErrorउठाया जाता है। यह डॉक्स में उल्लिखित है:

यदि डेटा रिक्त है, या यदि कोई सबसे सामान्य मूल्य नहीं है, तो AnalyticsError उठाया जाता है।

लेकिन आप अपने लिए देख सकते हैं ...

statistics.mode([1, 2])
# ---------------------------------------------------------------------------
# StatisticsError                           Traceback (most recent call last)
# ...
# StatisticsError: no unique mode; found 2 equally common values

@JoshFriedlander df.groupby(cols).agg(pd.Series.mode)मेरे लिए काम करने लगता है। अगर वह काम नहीं करता है, तो मेरा दूसरा अनुमान होगा df.groupby(cols).agg(lambda x: pd.Series.mode(x).values[0])
cs95

धन्यवाद (हमेशा की तरह!) आपका दूसरा विकल्प मेरे लिए चीजों में सुधार करता है, लेकिन मुझे एक मिल रहा है IndexError: index 0 is out of bounds for axis 0 with size 0(शायद इसलिए कि ऐसे समूह हैं जिनमें एक श्रृंखला में केवल NaN हैं)। जोड़ा जा रहा है dropna=Falseहल करती है यह , लेकिन उठाने के लिए लगता है '<' not supported between instances of 'float' and 'str'(मेरी श्रृंखला तार है)। (यदि आप चाहें तो इसे एक नए प्रश्न में बनाने के लिए खुश हैं।)
जोश फ्रीडलैंडर

2
@JoshFriedlander परिभाषित करें def foo(x): m = pd.Series.mode(x); return m.values[0] if not m.empty else np.nanऔर फिर उपयोग करें df.groupby(cols).agg(foo)। यदि वह काम नहीं करता है, तो fooथोड़ा सा लागू करने के साथ बेला । यदि आपको अभी भी परेशानी हो रही है, तो मैं एक नया Q खोलने की सलाह देता हूं।
cs95

1
मुझे यह जोड़ना चाहिए कि यदि आप गिनती शामिल करना चाहते हैं np.nan, तो कोई भी इसे df.groupy(cols).agg(lambda x: x.mode(dropna=False).iloc[0])मोड के लिए कर सकता है, यह मानते हुए कि आप संबंधों के बारे में परवाह नहीं करते हैं और सिर्फ एक मोड चाहते हैं।
इरिन

17

के लिए agg, लैंबा फ़ंक्शन को एक मिलता है Series, जिसमें एक 'Short name'विशेषता नहीं होती है ।

stats.mode दो सरणियों का एक टपल देता है, इसलिए आपको इस टपल में पहले एरे का पहला तत्व लेना है।

इन दो सरल परिवर्तनों के साथ:

source.groupby(['Country','City']).agg(lambda x: stats.mode(x)[0][0])

रिटर्न

                         Short name
Country City                       
Russia  Sankt-Petersburg        Spb
USA     New-York                 NY

1
@ViacheslavNefedov - हाँ, लेकिन @ HYIE का घोल लें, जिसमें शुद्ध पांडा का उपयोग होता है। की कोई जरूरत नहीं scipy.stats
इमरू

14

यहां खेल में थोड़ी देर हो गई, लेकिन मैं HYRY के समाधान के साथ कुछ प्रदर्शन मुद्दों में चल रहा था, इसलिए मुझे एक दूसरे के साथ आना पड़ा।

यह प्रत्येक कुंजी-मूल्य की आवृत्ति का पता लगाकर काम करता है, और फिर, प्रत्येक कुंजी के लिए, केवल उस मूल्य को रखता है जो इसके साथ सबसे अधिक बार दिखाई देता है।

एक अतिरिक्त समाधान भी है जो कई मोड का समर्थन करता है।

मेरे द्वारा काम कर रहे डेटा के प्रतिनिधि के पैमाने पर, यह 37.4s से 0.5s तक के रनटाइम को कम कर देता है!

यहाँ समाधान के लिए कोड, कुछ उदाहरण उपयोग, और स्केल परीक्षण है:

import numpy as np
import pandas as pd
import random
import time

test_input = pd.DataFrame(columns=[ 'key',          'value'],
                          data=  [[ 1,              'A'    ],
                                  [ 1,              'B'    ],
                                  [ 1,              'B'    ],
                                  [ 1,              np.nan ],
                                  [ 2,              np.nan ],
                                  [ 3,              'C'    ],
                                  [ 3,              'C'    ],
                                  [ 3,              'D'    ],
                                  [ 3,              'D'    ]])

def mode(df, key_cols, value_col, count_col):
    '''                                                                                                                                                                                                                                                                                                                                                              
    Pandas does not provide a `mode` aggregation function                                                                                                                                                                                                                                                                                                            
    for its `GroupBy` objects. This function is meant to fill                                                                                                                                                                                                                                                                                                        
    that gap, though the semantics are not exactly the same.                                                                                                                                                                                                                                                                                                         

    The input is a DataFrame with the columns `key_cols`                                                                                                                                                                                                                                                                                                             
    that you would like to group on, and the column                                                                                                                                                                                                                                                                                                                  
    `value_col` for which you would like to obtain the mode.                                                                                                                                                                                                                                                                                                         

    The output is a DataFrame with a record per group that has at least one mode                                                                                                                                                                                                                                                                                     
    (null values are not counted). The `key_cols` are included as columns, `value_col`                                                                                                                                                                                                                                                                               
    contains a mode (ties are broken arbitrarily and deterministically) for each                                                                                                                                                                                                                                                                                     
    group, and `count_col` indicates how many times each mode appeared in its group.                                                                                                                                                                                                                                                                                 
    '''
    return df.groupby(key_cols + [value_col]).size() \
             .to_frame(count_col).reset_index() \
             .sort_values(count_col, ascending=False) \
             .drop_duplicates(subset=key_cols)

def modes(df, key_cols, value_col, count_col):
    '''                                                                                                                                                                                                                                                                                                                                                              
    Pandas does not provide a `mode` aggregation function                                                                                                                                                                                                                                                                                                            
    for its `GroupBy` objects. This function is meant to fill                                                                                                                                                                                                                                                                                                        
    that gap, though the semantics are not exactly the same.                                                                                                                                                                                                                                                                                                         

    The input is a DataFrame with the columns `key_cols`                                                                                                                                                                                                                                                                                                             
    that you would like to group on, and the column                                                                                                                                                                                                                                                                                                                  
    `value_col` for which you would like to obtain the modes.                                                                                                                                                                                                                                                                                                        

    The output is a DataFrame with a record per group that has at least                                                                                                                                                                                                                                                                                              
    one mode (null values are not counted). The `key_cols` are included as                                                                                                                                                                                                                                                                                           
    columns, `value_col` contains lists indicating the modes for each group,                                                                                                                                                                                                                                                                                         
    and `count_col` indicates how many times each mode appeared in its group.                                                                                                                                                                                                                                                                                        
    '''
    return df.groupby(key_cols + [value_col]).size() \
             .to_frame(count_col).reset_index() \
             .groupby(key_cols + [count_col])[value_col].unique() \
             .to_frame().reset_index() \
             .sort_values(count_col, ascending=False) \
             .drop_duplicates(subset=key_cols)

print test_input
print mode(test_input, ['key'], 'value', 'count')
print modes(test_input, ['key'], 'value', 'count')

scale_test_data = [[random.randint(1, 100000),
                    str(random.randint(123456789001, 123456789100))] for i in range(1000000)]
scale_test_input = pd.DataFrame(columns=['key', 'value'],
                                data=scale_test_data)

start = time.time()
mode(scale_test_input, ['key'], 'value', 'count')
print time.time() - start

start = time.time()
modes(scale_test_input, ['key'], 'value', 'count')
print time.time() - start

start = time.time()
scale_test_input.groupby(['key']).agg(lambda x: x.value_counts().index[0])
print time.time() - start

इस कोड को चलाने पर कुछ प्रिंट होगा:

   key value
0    1     A
1    1     B
2    1     B
3    1   NaN
4    2   NaN
5    3     C
6    3     C
7    3     D
8    3     D
   key value  count
1    1     B      2
2    3     C      2
   key  count   value
1    1      2     [B]
2    3      2  [C, D]
0.489614009857
9.19386196136
37.4375009537

उम्मीद है की यह मदद करेगा!


सबसे तेज़ तरीका मैं साथ आता हूँ .. धन्यवाद!
21

1
वहाँ इस aproach का उपयोग करने के लिए एक रास्ता है, लेकिन सीधे agg मापदंडों के अंदर ?, जैसे। agg({'f1':mode,'f2':np.sum})
पाब्लो

1
@ पाब्लो दुर्भाग्य से नहीं, क्योंकि इंटरफ़ेस काफी समान नहीं है। मैं इसे एक अलग ऑपरेशन के रूप में करने की सलाह देता हूं, और फिर अपने परिणामों में शामिल हो रहा हूं। और निश्चित रूप से, यदि प्रदर्शन चिंता का विषय नहीं है, तो आप अपने कोड को अधिक संक्षिप्त रखने के लिए HYRY के समाधान का उपयोग कर सकते हैं।
1833 में

@ abw333 मैंने HYRY के समाधान का उपयोग किया, लेकिन मैं प्रदर्शन के मुद्दों में भाग लेता हूं ... मुझे उम्मीद है कि पांडा देव टीम aggविधि में अधिक कार्यों का समर्थन करती है।
पाब्लो

निश्चित रूप से बड़े डेटाफ़्रेम के लिए जाने का रास्ता। मेरे पास 83M पंक्तियाँ और 2.5M अद्वितीय समूह थे। इसने प्रति कॉलम 28 सेकंड का समय लिया, जबकि एग ने 11 मिनट प्रति कॉलम का समय लिया।
एलॉल्ज़

5

यहाँ दो शीर्ष उत्तर सुझाते हैं:

df.groupby(cols).agg(lambda x:x.value_counts().index[0])

या, अधिमानतः

df.groupby(cols).agg(pd.Series.mode)

हालाँकि, ये दोनों साधारण किनारे के मामलों में विफल हैं, जैसा कि यहाँ दिखाया गया है:

df = pd.DataFrame({
    'client_id':['A', 'A', 'A', 'A', 'B', 'B', 'B', 'C'],
    'date':['2019-01-01', '2019-01-01', '2019-01-01', '2019-01-01', '2019-01-01', '2019-01-01', '2019-01-01', '2019-01-01'],
    'location':['NY', 'NY', 'LA', 'LA', 'DC', 'DC', 'LA', np.NaN]
})

सबसे पहला:

df.groupby(['client_id', 'date']).agg(lambda x:x.value_counts().index[0])

पैदावार IndexError(समूह द्वारा लौटाई गई खाली श्रृंखला के कारण C)। द्वितीय:

df.groupby(['client_id', 'date']).agg(pd.Series.mode)

रिटर्न ValueError: Function does not reduce, चूंकि पहला समूह दो की सूची देता है (क्योंकि दो मोड हैं)। (जैसा कि यहां दस्तावेज किया गया है , अगर पहले समूह ने एक भी मोड लौटाया तो यह काम करेगा!)

इस मामले के दो संभावित समाधान हैं:

import scipy
x.groupby(['client_id', 'date']).agg(lambda x: scipy.stats.mode(x)[0])

और CS95 द्वारा मुझे दिए गए समाधान यहाँ टिप्पणियों में दिए गए हैं :

def foo(x): 
    m = pd.Series.mode(x); 
    return m.values[0] if not m.empty else np.nan
df.groupby(['client_id', 'date']).agg(foo)

हालांकि, ये सभी धीमे हैं और बड़े डेटासेट के लिए अनुकूल नहीं हैं। एक समाधान जिसका मैंने उपयोग किया था, a) इन मामलों से निपट सकता है और b) बहुत, बहुत तेज है, abw33 के उत्तर का हल्का संशोधित संस्करण है (जो अधिक होना चाहिए):

def get_mode_per_column(dataframe, group_cols, col):
    return (dataframe.fillna(-1)  # NaN placeholder to keep group 
            .groupby(group_cols + [col])
            .size()
            .to_frame('count')
            .reset_index()
            .sort_values('count', ascending=False)
            .drop_duplicates(subset=group_cols)
            .drop(columns=['count'])
            .sort_values(group_cols)
            .replace(-1, np.NaN))  # restore NaNs

group_cols = ['client_id', 'date']    
non_grp_cols = list(set(df).difference(group_cols))
output_df = get_mode_per_column(df, group_cols, non_grp_cols[0]).set_index(group_cols)
for col in non_grp_cols[1:]:
    output_df[col] = get_mode_per_column(df, group_cols, col)[col].values

अनिवार्य रूप से, विधि एक समय में एक कर्नल पर काम करती है और एक df को आउटपुट करती है, इसलिए इसके बजाय concat, जो गहन है, आप पहले को df मानते हैं, और फिर values.flatten()df में स्तंभ के रूप में iteratively आउटपुट सरणी ( ) जोड़ते हैं ।


3

औपचारिक रूप से, सही उत्तर @eumiro समाधान है। @HYRY समाधान की समस्या यह है कि जब आपके पास संख्याओं का एक क्रम होता है जैसे कि [1,2,3,4] समाधान गलत है, अर्थात आपके पास मोड नहीं है । उदाहरण:

>>> import pandas as pd
>>> df = pd.DataFrame(
        {
            'client': ['A', 'B', 'A', 'B', 'B', 'C', 'A', 'D', 'D', 'E', 'E', 'E', 'E', 'E', 'A'], 
            'total': [1, 4, 3, 2, 4, 1, 2, 3, 5, 1, 2, 2, 2, 3, 4], 
            'bla': [10, 40, 30, 20, 40, 10, 20, 30, 50, 10, 20, 20, 20, 30, 40]
        }
    )

यदि आप @ प्राप्त करते हैं जैसे आप प्राप्त करते हैं:

>>> print(df.groupby(['client']).agg(lambda x: x.value_counts().index[0]))
        total  bla
client            
A           4   30
B           4   40
C           1   10
D           3   30
E           2   20

जो स्पष्ट रूप से गलत है ( मान देखें जो 1 होना चाहिए और 4 नहीं ) क्योंकि यह अद्वितीय मानों के साथ संभाल नहीं सकता है।

इस प्रकार, अन्य समाधान सही है:

>>> import scipy.stats
>>> print(df.groupby(['client']).agg(lambda x: scipy.stats.mode(x)[0][0]))
        total  bla
client            
A           1   10
B           4   40
C           1   10
D           3   30
E           2   20

1

यदि आप इसे हल करने के लिए एक और दृष्टिकोण चाहते हैं जो निर्भर नहीं करता है value_countsया scipy.statsआप Counterसंग्रह का उपयोग कर सकते हैं

from collections import Counter
get_most_common = lambda values: max(Counter(values).items(), key = lambda x: x[1])[0]

जिसे उपरोक्त उदाहरण पर लागू किया जा सकता है

src = pd.DataFrame({'Country' : ['USA', 'USA', 'Russia','USA'], 
              'City' : ['New-York', 'New-York', 'Sankt-Petersburg', 'New-York'],
              'Short_name' : ['NY','New','Spb','NY']})

src.groupby(['Country','City']).agg(get_most_common)

यह pd.Series.modeया की तुलना में तेज है pd.Series.value_counts().iloc[0]- लेकिन अगर आपके पास NaN मान है जिसे आप गिनना चाहते हैं, तो यह विफल हो जाएगा। प्रत्येक NaN घटना को अन्य NaN से भिन्न के रूप में देखा जाएगा, इसलिए प्रत्येक NaN की गणना की जाती है 1। देखें stackoverflow.com/questions/61102111/...
आइरीन

1

यदि आप NaN मानों को शामिल नहीं करना चाहते हैं , तो इसका उपयोग Counterकरना pd.Series.modeया की तुलना में बहुत तेज़ है pd.Series.value_counts()[0]:

def get_most_common(srs):
    x = list(srs)
    my_counter = Counter(x)
    return my_counter.most_common(1)[0][0]

df.groupby(col).agg(get_most_common)

कार्य करना चाहिए। यह तब विफल होगा जब आपके पास NaN मान होगा, क्योंकि प्रत्येक NaN को अलग से गिना जाएगा।


0

समस्या यहां प्रदर्शन है, यदि आप पंक्तियों की एक बहुत कुछ है यह एक समस्या हो जाएगा।

यदि यह आपका मामला है, तो कृपया इसके साथ प्रयास करें:

import pandas as pd

source = pd.DataFrame({'Country' : ['USA', 'USA', 'Russia','USA'], 
              'City' : ['New-York', 'New-York', 'Sankt-Petersburg', 'New-York'],
              'Short_name' : ['NY','New','Spb','NY']})

source.groupby(['Country','City']).agg(lambda x:x.value_counts().index[0])

source.groupby(['Country','City']).Short_name.value_counts().groupby['Country','City']).first()

0

बड़े डेटासेट के लिए एक थोड़ा अनाड़ी लेकिन तेजी से दृष्टिकोण में ब्याज के एक कॉलम के लिए मायने रखना शामिल है, सबसे अधिक से सबसे कम तक की गिनती को छांटना, और फिर सबसे बड़े मामलों को बनाए रखने के लिए एक सबसेट पर डी-डुप्लिकेट करना। कोड उदाहरण निम्नलिखित है:

>>> import pandas as pd
>>> source = pd.DataFrame(
        {
            'Country': ['USA', 'USA', 'Russia', 'USA'], 
            'City': ['New-York', 'New-York', 'Sankt-Petersburg', 'New-York'],
            'Short name': ['NY', 'New', 'Spb', 'NY']
        }
    )
>>> grouped_df = source\
        .groupby(['Country','City','Short name'])[['Short name']]\
        .count()\
        .rename(columns={'Short name':'count'})\
        .reset_index()\
        .sort_values('count', ascending=False)\
        .drop_duplicates(subset=['Country', 'City'])\
        .drop('count', axis=1)
>>> print(grouped_df)
  Country              City Short name
1     USA          New-York         NY
0  Russia  Sankt-Petersburg        Spb
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.