I was looking to speed up processing of a groupby operation and while it now processes much faster, the resulting dataframe is not what I want.
Make MultiIndexed dataframe with some data:
import pandas as pd
import numpy as np
import cython
data = np.round(np.random.randn(4, 3), 1)
df = pd.DataFrame(data, columns=pd.MultiIndex.from_product([['TOP'], ['A', 'B','C']]))
df
My cython function looks like this:
%load_ext cython
%%cython
import numpy as np
def func4(x):
result = np.zeros([len(x)])
for i in range(len(x)):
result[i] = x[i][2] + x[i][1] - x[i][0]
return result
And when I execute this function:
g = df.groupby(level=0, axis=1).apply(lambda x: func4(x.to_numpy()))
I get a series with all values in one cell:
What I would like to get instead is an indexed series like this:
I am able to get the result I want if I convert result to a dataframe at the end of the function, but then the groupby + apply becomes slow again:
def func4(x):
result = np.zeros([len(x)])
for i in range(len(x)):
result[i] = x[i][2] + x[i][1] - x[i][0]
return pd.DataFrame(result)
EDIT:
You can use this to produce a large dataframe for testing:
import pandas as pd
import itertools
import numpy as np
import string
import cython
col = [f'{a}{b}{c}' for a,b,c in list(itertools.product(string.ascii_uppercase[:20], repeat = 3))]
col1 = [str(i) for i in range(1, 10)] # change the 10 to a higher number to produce even more data
col2 = list(i for i in string.ascii_uppercase[:3])
all_keys = ['.'.join(i) for i in itertools.product(col, col1, col2)]
rng = pd.date_range(end=pd.Timestamp.today().date(), periods=6, freq='M')
df = pd.DataFrame(np.random.randint(0, 1000, size=(len(rng), len(all_keys))), columns=all_keys, index=rng)
def top_key(x):
split = x.split('.', 2)
return f'{split[0]}.{split[1]}'
df.columns = pd.MultiIndex.from_tuples([(top_key(c), c) for c in df.columns])
print(len(df.columns.get_level_values(0).unique()))
df.head(5)
UPDATE 2, performance of solutions:
Fastest, but resulting series has the issue described above.






