2
\$\begingroup\$

I have a dataframe with the columns "Action", "Filt" and "Val" which measure the resulting error of filtering an action. I want to calculate the mean of the error for each "Action" and "Filt". Currently, I do this by iterating through each combination of "Action" and "Filt". However, I'm afraid the slicing is taking up more time than I would like.

import pandas as pd

in_dat = [
    ("run", "raw", 1),
    ("run", "deep", 1),
    ("jump", "raw", 2),
    ("jump", "deep", 2),
    ("run", "raw", 2),
    ("run", "deep", 2),
    ("jump", "raw", 3),
    ("jump", "deep", 3)

]


all_err = pd.DataFrame(in_dat, columns=("action", "filt", "val"))

mean_vals = []

for act in tuple(all_err.action.unique()):
    for filt in tuple(all_err.filt.unique()):
        mean_val = all_err[(all_err.action == act) & (all_err.filt == filt)].val.mean()
        mean_vals.append((act, filt, mean_val))


mean_err = pd.DataFrame(mean_vals, columns=("Action", "Filt", "Error"))

This gives the result of mean_err being:

  Action  Filt  Error
0    run   raw    1.5
1    run  deep    1.5
2   jump   raw    2.5
3   jump  deep    2.5

What's a faster way to do this with Pandas and getting a similar result?

\$\endgroup\$
1
\$\begingroup\$

pandas is deep

The pandas toolkit is very deep and can take a while to grok some of the basics. In the case of your code, you are replicating the functionality in pandas.DataFrame.groupby(). groupby gathers all of the elements together with matching values in the fields indicated and then allows those elements to be worked on as a group. In the case of:

for act in tuple(all_err.action.unique()):
    for filt in tuple(all_err.filt.unique()):

This code is referencing all values of the action and filt columns into two variables. Then:

mean_val = all_err[(all_err.action == act) & (all_err.filt == filt)].val.mean()
mean_vals.append((act, filt, mean_val))

These lines perform a mean on the val column for each row that matches in the two grouped columns, and stores those results into a list. Pandas has a shortcut for these sorts of operations called groupby. The above four lines, plus the list initialization, can be equivalently written as:

all_err.groupby(['action', 'filt']).val.mean()

How?

Starting with:

all_err.groupby(['action', 'filt'])

produces a pandas.core.groupby.DataFrameGroupBy. This object instance understands how to iterate over the dataframe a group at a time, allowing access to these groups in various ways. So then,

all_err.groupby(['action', 'filt']).val

selects the val column to work against, and finally,

all_err.groupby(['action', 'filt']).val.mean()

asks for the mean value of that column.

Results:

mean_err = all_err.groupby(['action', 'filt']).val.mean()
print(mean_err)

  Action  Filt  Error
0    run   raw    1.5
1    run  deep    1.5
2   jump   raw    2.5
3   jump  deep    2.5
|improve this answer|||||
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.