Python Pandas Dataframe code takes too long to finish
You could do the entire thing in pandas and numpy as their Cython implementation will be much faster than iterating over objects in Python. 

First simulate some data,

import pandas as pd, numpy as np
from datetime import datetime

num_samples = 50000
user_ids = np.arange(num_samples)
dates = pd.date_range("2012-01-01","2015-01-01")
start_dates = dates[np.random.randint(0,len(dates),50000)]

data = pd.DataFrame(data={"user_id": user_ids, "start_date":start_dates})
data["end_date"] = data.start_date.apply(lambda x: x + pd.DateOffset(days=np.random.randint(0,300)))


which results in data:

start_date  user_id   end_date
2013-12-15        0   2014-09-24 
2013-12-13        1   2014-01-17
2014-08-29        2   2015-03-25
2014-04-13        3   2015-01-04 
2014-01-21        4   2014-06-22


We need an output dataframe so that we don't have to painfully iterate over each date one by one:

output = pd.DataFrame({"date":pd.date_range(data.start_date.min(), datetime.now())})


and now define your functions to calculate the churn rate and apply it over the entire data frame:

def get_users_from_date(df, date):
    return df[(date &gt;= df.start_date) &amp; (date &lt;= df.end_date)].user_id

def calc_churn(df, date):
    users_before = get_users_from_date(df,date-pd.DateOffset(months=1))
    users_now = get_users_from_date(df,date)

    if len(users_before):
            return len(np.setdiff1d(users_before,users_now)) / len(users_before)
    else:
            return 0

output["churn"] = output.date.apply(lambda x: calc_churn(data,x))


This finishes for me in just:


  5.42 s ± 65.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)


Since we are using np.setdiff1d to find the difference, we don't need to unique each subset of user ids beforehand. Finding the unique subsets in get_user_from_date then passing them in, even with assume_unique=True, gets a time of:


  5.98 s ± 75 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)


while using Python set difference instead of numpy arrays gets a time of:


  7.5 s ± 114 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)


and finally, combining Python sets with list comprehensions, similar to your current implementation never finished running.

The main bottleneck is in comparing the sets of user ids, so doing that in either numpy or pandas instead of iterating over Python objects will improve performance.

On a different note, your code looks good and is really easy to read but you should use snake case instead of camel case to fit Python convention.