How to do data analysis (like counts, ucounts, frequency) with pandas?

Viewed 146

I have DataFrame like below:

df = pd.DataFrame([
    ("i", 1, 'GlIrbixGsmCL'),
    ("i", 1, 'GlIrbixGsmCL'),
    ("i", 1, '3IMR1UteQA'),
    ("c", 1, 'GlIrbixGsmCL'),
    ("i", 2, 'GlIrbixGsmCL'),
], columns=['type', 'cid', 'userid'])

Expected output like: expect output

For more details:

i_counts, c_counts      => df.groupby(["cid","type"]).size()
i_ucounts, c_ucounts    => df.groupby(["cid","type"])["userid"].nunique()
i_frequency,u_frequency => df.groupby(["cid","type"])["userid"].value_counts()

Looks it's a little complex for me, how to do with pandas to get the expected result?

The related screenshots: screenshots

2 Answers

This is how I would approach this:

aggfuncs= {
    'counts': ('userid', 'count'), 
    'ucounts': ('userid', 'nunique'),
    'frequency': ('userid', lambda S: S.value_counts().to_dict()),
}

output = df.groupby(['cid', 'type']).agg(**aggfuncs).unstack()
output.columns = output.columns.map(lambda tup: '_'.join(tup[::-1]))

output:

     c_counts  i_counts  c_ucounts  i_ucounts          c_frequency                           i_frequency
cid
1         1.0       3.0        1.0        2.0  {'GlIrbixGsmCL': 1}  {'GlIrbixGsmCL': 2, '3IMR1UteQA': 1}
2         NaN       1.0        NaN        1.0                  NaN                   {'GlIrbixGsmCL': 1}

I think that is the core of what you want. You will need some cosmetic amendments to get the output exactly as in your example (e.g. fillna etc.).

STEPS:

  1. Extract the id_numbers from user_id and convert them to int type.
  2. Use groupby and agg to evaluate count/ucount / `frequency.
  3. use pivot to restructure the table.
  4. flatten the columns and reset_index if required.
df['userid'] = df.userid.str.extract(r'(\d+)').astype(int)
k = df.groupby(["type", 'cid']).agg(count=('userid', 'count'), ucount=(
    'userid', 'nunique'), frequency=('userid', lambda x: x.value_counts().to_dict())).reset_index()
k = k.pivot(index=[k.index, 'cid'], columns='type').fillna(0)

OUTPUT:

      count      ucount      frequency              
type      c    i      c    i         c             i
  cid                                               
0 1     1.0  0.0    1.0  0.0    {1: 1}             0
1 1     0.0  3.0    0.0  2.0         0  {1: 2, 2: 1}
2 2     0.0  1.0    0.0  1.0         0        {1: 1}

Then to transform columns:

k.columns = k.columns.map(lambda x: '_'.join(x[::-1]))

OUTPUT:

       c_count  i_count  c_ucount  i_ucount c_frequency   i_frequency
  cid                                                                
0 1        1.0      0.0       1.0       0.0      {1: 1}             0
1 1        0.0      3.0       0.0       2.0           0  {1: 2, 2: 1}
2 2        0.0      1.0       0.0       1.0           0        {1: 1}

updated answer (according to your edited question):

k = df.groupby(["type" , 'cid']).agg(count = ('userid' ,'count') , ucount = ('userid', 'nunique') , frequency=('userid', lambda x: x.value_counts().to_dict())).reset_index()
k = k.pivot(index=['cid'], columns ='type').fillna(0)

OUTPUT:

    count   ucount  frequency
type    c   i   c   i   c   i
cid                     
1   1.0 3.0 1.0 2.0 {'userid001': 1}    {'userid001': 2, 'userid002': 1}
2   0.0 1.0 0.0 1.0 0   {'userid001': 1}

NOTE: use df.userid = df.userid.factorize()[0] to encode userid if required.

Related