Create new DataFrame containing mean information
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) &amp; (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