Simply put, I want to change the following code into a funtion that doesn't use apply or progress_apply, so that the performance doesn't take 4+ hours to execute on 20 million+ rows.
d2['B'] = d2['C'].progress_apply(lambda x: [z for y in d1['B'] for z in y if x.startswith(z)])
d2['B'] = d2['B'].progress_apply(max)
Full question below:
I have two dataframes. The first dataframe has a column with 4 categories (A,B,C,D) with four different lists of numbers that I want to compare against a column in the second dataframe, which is not a list like in the first dataframe but instead just a single value that will start with one or more values from the first dataframe. As such, after executing some list comprehension to return a list of matching values in a new column in the second dataframe, the final step is to get the max of those values per list per row:
d1 = pd.DataFrame({'A' : ['A', 'B', 'C', 'D'],
'B' : [['84'], ['8420', '8421', '8422', '8423', '8424', '8425', '8426'], ['847', '8475'], ['8470', '8471']]})
A B
0 A [84]
1 B [8420, 8421, 8422, 8423, 8424, 8425, 8426]
2 C [847, 8475]
3 D [8470, 8471]
d2 = pd.DataFrame({'C' : [8420513, 8421513, 8426513, 8427513, 8470513, 8470000, 8475000]})
C
0 8420513
1 8421513
2 8426513
3 8427513
4 8470513
5 8470000
6 8475000
My current code is this:
from tqdm import tqdm, tqdm_notebook
tqdm_notebook().pandas()
d1 = pd.DataFrame({'A' : ['A', 'B', 'C', 'D'], 'B' : [['84'], ['8420', '8421', '8422', '8423', '8424', '8425', '8426'], ['847', '8475'], ['8470', '8471']]})
d2 = pd.DataFrame({'C' : [8420513, 8421513, 8426513, 8427513, 8470513, 8470000, 8475000]})
d2['C'] = d2['C'].astype(str)
d2['B'] = d2['C'].progress_apply(lambda x: [z for y in d1['B'] for z in y if x.startswith(z)])
d2['B'] = d2['B'].progress_apply(max)
d2
and successfully returns this output:
C B
0 8420513 8420
1 8421513 8421
2 8426513 8426
3 8427513 84
4 8470513 8470
5 8470000 8470
6 8475000 8475
The problem lies with the fact that the tqdm progress bar is estimating the code will take 4-5 hours to run on my actual DataFrame with 20 million plus rows. I know that .apply should be avoided and that a custom function can be much faster, so that I don't have to go row-by-row. I can usually change apply to a function, but I am struggling with this particular one. I think I am far away, but I will share what I have tried:
def func1(df, d2C, d1B):
return df[[z for y in d1B for z in y if z in d2C]]
d2['B'] = func1(d2, d2['C'], d1['B'])
d2
With this code, I am receiving ValueError: Wrong number of items passed 0, placement implies 1 and also still need to include code to get the max of each list per row.

