<jupyter_start><jupyter_text>Megaline Project  As an analyst for Megaline, my goal of this project is figure out which of the two plans they currently offer (Surf and Ultimate) bring in the most revenue and are the most profitable. This will help the commercial department adjust the advertising budget for the upcoming year.  Importing Packages<jupyter_code>import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats as st
!pip install tabulate
from tabulate import tabulate
import scipy.stats as stats
from scipy.stats import zscore
import warnings
warnings.filterwarnings('ignore')
!pip install pandas-profiling[notebook]
from pandas_profiling import ProfileReport
import seaborn as sns<jupyter_output>Requirement already satisfied: tabulate in c:\users\bnoah\anaconda3\lib\site-packages (0.8.9)
Requirement already satisfied: pandas-profiling[notebook] in c:\users\bnoah\anaconda3\lib\site-packages (3.1.1)
Requirement already satisfied: pandas!=1.0.0,!=1.0.1,!=1.0.2,!=1.1.0,>=0.25.3 in c:\users\bnoah\anaconda3\lib\site-packages (from pandas-profiling[notebook]) (1.3.4)
Requirement already satisfied: tqdm>=4.48.2 in c:\users\bnoah\anaconda3\lib\site-packages (from pandas-profiling[notebook]) (4.62.3)
Requirement already satisfied: phik>=0.11.1 in c:\users\bnoah\appdata\roaming\python\python38\site-packages (from pandas-profiling[notebook]) (0.12.0)
Requirement already satisfied: pydantic>=1.8.1 in c:\users\bnoah\appdata\roaming\python\python38\site-packages (from pandas-profiling[notebook]) (1.8.2)
Requirement already satisfied: scipy>=1.4.1 in c:\users\bnoah\anaconda3\lib\site-packages (from pandas-profiling[notebook]) (1.7.1)
Requirement already satisfied: seaborn>=0.10.1 in c:\users\[...]<jupyter_text>Details | Package          | Description                                                          ||:-----------------|:---------------------------------------------------------------------|| pandas           | For data preprocessing and basic descriptive statistics.             || matplotlib       | For creating graphs.                                                 || numpy            | For scientific computing.                                            || scipy            | For calculating statistics.                                          || tabulate         | For creating tables.                                                 || pandas_profiling | For creating a profile report on the dataset.                        || warnings         | For ignoring warnings after finishing the project for readability.   || seaborn          | For creating graphs.                                                 |  Importing and Cleaning Data   Importing Calls Data<jupyter_code>#read the data
try: 
    calls_df = pd.read_csv('/Users/bnoah/data/megaline_calls.csv')
except: 
    calls_df = pd.read_csv('/datasets/megaline_calls.csv')<jupyter_output><empty_output><jupyter_text>Profile Report<jupyter_code>calls_df_report = ProfileReport(calls_df, title="Calls Profiling Report")
calls_df_report.to_widgets()<jupyter_output><empty_output><jupyter_text>Conclusions - For the calls data, we have **137,735** observations representing unique phone calls. - The `duration` variable is a decimal value and will have to be rounded up to calculate the costs per phone call.- `call_date` will need to be converted to datetime format. - There are no duplicated `id` values, so we can treat this as a unique identifier for calls.- There are **26,834** instances where `call_duration` is zero.   Importing Internet Data<jupyter_code>#read the data
try: 
    internet_df = pd.read_csv('/Users/bnoah/data/megaline_internet.csv')
except: 
    internet_df = pd.read_csv('/datasets/megaline_internet.csv')<jupyter_output><empty_output><jupyter_text>Profile Report<jupyter_code>internet_df_report = ProfileReport(internet_df, title="Internet Profiling Report")
internet_df_report.to_widgets()<jupyter_output><empty_output><jupyter_text>Conclusions - For the internet data, we had **104,825** observations representing unique internet sessions. - The `mb_used` will have to be converted to **GBs** and rounded up to calculate the monthly internet costs. - `session_date` will need to be converted to datetime format. - There are no duplicated `id` values, so we can treat this as a unique identifier for internet uses.- There are **13,747** instances where `mb_used` is zero.   Importing Messages Data<jupyter_code>#read the data
try: 
    messages_df = pd.read_csv('/Users/bnoah/data/megaline_messages.csv')
except: 
    messages_df = pd.read_csv('/datasets/megaline_messages.csv')<jupyter_output><empty_output><jupyter_text>Profile Report<jupyter_code>messages_df_report = ProfileReport(messages_df, title="Messages Profiling Report")
messages_df_report.to_widgets()<jupyter_output><empty_output><jupyter_text>Conclusions - For the messages data, we had **76,051** observations representing unique text messages. - The `message_date` variable will need to be converted to datetime format. - There are no duplicated `id` values, so we can treat this as a unique identifier for messages.   Importing Users Data<jupyter_code>#read the data
try: 
    users_df = pd.read_csv('/Users/bnoah/data/megaline_users.csv')
except: 
    users_df = pd.read_csv('/datasets/megaline_users.csv')<jupyter_output><empty_output><jupyter_text>Profile Report<jupyter_code>users_df_report = ProfileReport(users_df, title="Users Profiling Report")
users_df_report.to_widgets()<jupyter_output><empty_output><jupyter_text>Conclusions - For the users data, we had **500** observations representing unique individuals on one of the two phone plans. - The `reg_date` variable will need to be converted to datetime format. - There are no duplicated `user_id` values, so we can treat this as a unique identifier for users that can be matched on with the previous datasets.   Importing Plans Data<jupyter_code>#read the data
try: 
    plan_df = pd.read_csv('/Users/bnoah/data/megaline_plans.csv')
except: 
    plan_df = pd.read_csv('/datasets/megaline_plans.csv')
plan_df<jupyter_output><empty_output><jupyter_text>Conclusions The information in this dataset is used to calculate each users monthly costs. I will use this information to calculate monthly costs later on in this project.  Renaming Date Column and Creating Type Variable<jupyter_code>calls_df.rename(columns={"call_date": "date", "duration":"call_duration"},inplace=True)
calls_df['type'] = 'calls'
internet_df.rename(columns={"session_date": "date"},inplace=True)
internet_df['type'] = 'internet'
messages_df.rename(columns={"message_date": "date"},inplace=True)
messages_df['type'] = 'messages'<jupyter_output><empty_output><jupyter_text>Conclusions I renamed the date column in each of the three usage dataframes to `date`. I did this so we can merge the datasets and have just one column with the date. I also, created a new column called `type`, so that we know the type of each usage when we merge the datasets.   Merging Usage Data<jupyter_code>#putting all usage types into one dataframe
usage_df = calls_df.append([internet_df,messages_df])
usage_df.drop(columns=['id'],inplace=True)
usage_df.reset_index(inplace=True,drop=True)
display(usage_df.head())
usage_df.info()<jupyter_output><empty_output><jupyter_text>Conclusions I merged the three usage datasets together here. I also dropped the `id` variable as we have confirmed each observation is unique. All in all, this new dataset gives us **318,611** observations of unique usage instances.   Changing Variable Formats<jupyter_code>#change date to datetime variable
usage_df['date'] = pd.to_datetime(usage_df['date'], format = '%Y-%m-%d')
usage_df.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 318611 entries, 0 to 318610
Data columns (total 5 columns):
 #   Column         Non-Null Count   Dtype         
---  ------         --------------   -----         
 0   user_id        318611 non-null  int64         
 1   date           318611 non-null  datetime64[ns]
 2   call_duration  137735 non-null  float64       
 3   type           318611 non-null  object        
 4   mb_used        104825 non-null  float64       
dtypes: datetime64[ns](1), float64(2), int64(1), object(1)
memory usage: 12.2+ MB<jupyter_text>Conclusions I changed the `date` variable to datetime format. Every other variable seems to be in a reasonable data format, so I did not change them.  Removing Zero Values<jupyter_code>#notes: 26834 of the calls are 0 duration, 13747 of the internet sessions are 0 mbs. These must be errors because For calls, each individual call is rounded up: even if the call lasted just one second. Also, it is impossible to have a 0 gb web session.  
usage_df = usage_df[(usage_df['call_duration'] != 0) & (usage_df['mb_used'] != 0)]
usage_df.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
Int64Index: 278030 entries, 0 to 318610
Data columns (total 5 columns):
 #   Column         Non-Null Count   Dtype         
---  ------         --------------   -----         
 0   user_id        278030 non-null  int64         
 1   date           278030 non-null  datetime64[ns]
 2   call_duration  110901 non-null  float64       
 3   type           278030 non-null  object        
 4   mb_used        91078 non-null   float64       
dtypes: datetime64[ns](1), float64(2), int64(1), object(1)
memory usage: 12.7+ MB<jupyter_text>Conclusions In the dataset, there are **26,834**, and **13,747** instances where `call_duration` and `mb_used`, respectively, were zero. Missed calls might explain zero second duration calls and internet sessions done on wifi might explain zero mb sessions. Yet, I would immediately ask the data gatherer about what caused this, but since that is not possible, I decided to remove them from this analysis.   Prepare the Data  Totaling User Data<jupyter_code>#creating a dataframe with call duration rounded up for cost calculations
usage_df['call_duration'] = np.ceil(calls_df['call_duration'])
#creating pivot tables
user_totals_df = usage_df.pivot_table(index=['user_id'], values=['call_duration','mb_used','type'], aggfunc={'call_duration':['sum','count'], 'mb_used': 'sum', 'type': lambda x:(x=='messages').sum()})
user_totals_df.set_axis(['number_of_calls', 'total_mins', 'data_used', 'messages_sent'], axis='columns',inplace=True)
user_totals_df.head()<jupyter_output><empty_output><jupyter_text>Conclusions In this section I created a pivot table. I started out by rounding `call_duration` up to the nearest minute as this is what is used to calcuate the cost of a phone call. In these pivot tables, I summed the `call_duration`, and `mb_used` variables for each user, and counted the number of messages sent by each user.   Adding The User's Plan to the Tables<jupyter_code>#adding plan to the table
user_totals_df = user_totals_df.merge(users_df[['user_id','plan']],on='user_id',how='outer')
user_totals_df.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
Int64Index: 500 entries, 0 to 499
Data columns (total 6 columns):
 #   Column           Non-Null Count  Dtype  
---  ------           --------------  -----  
 0   user_id          500 non-null    int64  
 1   number_of_calls  490 non-null    float64
 2   total_mins       490 non-null    float64
 3   data_used        490 non-null    float64
 4   messages_sent    490 non-null    float64
 5   plan             500 non-null    object 
dtypes: float64(4), int64(1), object(1)
memory usage: 27.3+ KB<jupyter_text>Conclusions In this section, I added the `plan` variable from the user dataset to our overall user usage dataset.  Looking For Patterns Among Users Missing Usage Data<jupyter_code>#looking at users without usage data to see if there is any pattern
missing_users = users_df.merge(user_totals_df[user_totals_df['number_of_calls'].isna()]['user_id'], on='user_id', how='inner')
display(missing_users)
#removing users without usage data
user_totals_df = user_totals_df[user_totals_df['number_of_calls'].notnull()]<jupyter_output><empty_output><jupyter_text>Conclusions I noticed that 10 of the users had no usage data. After looking at these observations, I did not notice any patterns, and decided to drop them from the dataset.   Calculating Cost and Adding it As A Row<jupyter_code>def cost_calc(row):
    if row['plan'] == 'ultimate':
        return 70 + max(0, row['total_mins']-3000)*0.01 + max(0, row['messages_sent']-1000)*0.01 + max(0, np.ceil(row['data_used']/1024)-30)*7
    else:
        return 20 + max(0, row['total_mins']-500)*0.03 + max(0, row['messages_sent']-50)*0.03 + max(0, np.ceil(row['data_used']/1024)-15)*10

user_totals_df['cost'] = user_totals_df.apply(lambda row: cost_calc(row), axis=1)

display(user_totals_df.head())<jupyter_output><empty_output><jupyter_text>Conclusions I started out by creating a function that calculates the total cost depending on the individual's plan. This function returns the base cost plus any extra minutes, messages, and data used multiplied by the overuse fee described in the plans dataset. To calculate the data overuse charges, I had to round up the mbs used to the nearest gb. I did this by converting mbs to gbs and then rounding that number.   Analyze the data  Look at the Means by Plan<jupyter_code>user_totals_df.pivot_table(index='plan', values=['cost','total_mins','data_used','messages_sent'], aggfunc=['mean']).round()<jupyter_output><empty_output><jupyter_text>Conclusions I looked at the differences in means of our key variables among our two plans. The surf plan had a higher mean revenue with less data used, minutes used, and messages sent.  Look at the Histograms by Plan<jupyter_code>f, axs = plt.subplots(4, 2, figsize= (20, 20))
axs[0,0].hist(user_totals_df[user_totals_df['plan'] == 'ultimate']['cost'],bins=10,density=True)
axs[0,1].hist(user_totals_df[user_totals_df['plan'] == 'surf']['cost'],bins=10,density=True)
axs[1,0].hist(user_totals_df[user_totals_df['plan'] == 'ultimate']['total_mins'],bins=10,density=True)
axs[1,1].hist(user_totals_df[user_totals_df['plan'] == 'surf']['total_mins'],bins=10,density=True)
axs[2,0].hist(user_totals_df[user_totals_df['plan'] == 'ultimate']['data_used'],bins=10,density=True)
axs[2,1].hist(user_totals_df[user_totals_df['plan'] == 'surf']['data_used'],bins=10,density=True)
axs[3,0].hist(user_totals_df[user_totals_df['plan'] == 'ultimate']['messages_sent'],bins=10,density=True)
axs[3,1].hist(user_totals_df[user_totals_df['plan'] == 'surf']['messages_sent'],bins=10,density=True)
# set labels
plt.setp(axs[0,0], title='Ultimate Plan')
plt.setp(axs[0,1], title='Surf Plan')
plt.setp(axs[:], ylabel='Frequency')
plt.setp(axs[0:], xlabel='Cost')
plt.setp(axs[1:], xlabel='Total Minutes')
plt.setp(axs[2:], xlabel='Data Used')
plt.setp(axs[3:], xlabel='Messages Sent')
plt.suptitle("Histograms of Non-Bounded Variables Before Trimming")
plt.show()<jupyter_output><empty_output><jupyter_text>Conclusions Looking at the histograms above, we can notice that the costs histograms have large spikes at the base price for each plan. This is to be expected as there were always be a decent number of people who stay within the limits of the plan they purchase. Beyond that it looks like both histograms display right-skewed truncated distributional patterns. Next, looking at total minutes, we see right-skewed truncated distributional patterns. I though we might see large drops in frequency at certain points due to the artificial limits set by each plan, yet, the largest drop in frequency seems to be right before 2000 and 4000 minutes in the surf plan. I am unsure how to explain these drops in frequency, and would need to discuss this with the higher ups at the company to see if they have an explanation. As far as Data used, we also see a right-skewed truncated distribution patterns for both plans. Both plans seem to have their largest drop slightly after hitting 150,000 mbs. This again would be something that I would want to discuss with someone with more knowledge of the plans at the company.Lastly, the messages sent distributions also display right-skewed truncated patterns, and look nearly identical. This informs us that text messages do not seem to be impacted greatly based on the phone plan someone chooses.   Percent of Users Going Over Limit<jupyter_code>a = len(user_totals_df[(user_totals_df['plan'] == 'ultimate') & (user_totals_df['cost']>70)]) / len(user_totals_df[(user_totals_df['plan'] == 'ultimate')]) 
b = len(user_totals_df[(user_totals_df['plan'] == 'surf') & (user_totals_df['cost']>20)]) / len(user_totals_df[(user_totals_df['plan'] == 'surf')]) 
c = len(user_totals_df[(user_totals_df['plan'] == 'ultimate') & (user_totals_df['total_mins']>3000)]) / len(user_totals_df[(user_totals_df['plan'] == 'ultimate')])
d = len(user_totals_df[(user_totals_df['plan'] == 'surf') & (user_totals_df['total_mins']>500)]) / len(user_totals_df[(user_totals_df['plan'] == 'surf')]) 
e = len(user_totals_df[(user_totals_df['plan'] == 'ultimate') & (user_totals_df['data_used']>30720)]) / len(user_totals_df[(user_totals_df['plan'] == 'ultimate')])
f = len(user_totals_df[(user_totals_df['plan'] == 'surf') & (user_totals_df['data_used']>15360)]) / len(user_totals_df[(user_totals_df['plan'] == 'surf')]) 
g = len(user_totals_df[(user_totals_df['plan'] == 'ultimate') & (user_totals_df['messages_sent']>1000)]) / len(user_totals_df[(user_totals_df['plan'] == 'ultimate')])
h = len(user_totals_df[(user_totals_df['plan'] == 'surf') & (user_totals_df['messages_sent']>50)]) / len(user_totals_df[(user_totals_df['plan'] == 'surf')]) 

table = [['unit','ultimate','surf'],['cost','{0:.2%}'.format(a),'{0:.2%}'.format(b)],
         ['minutes','{0:.2%}'.format(c),'{0:.2%}'.format(d)],
         ['data','{0:.2%}'.format(e),'{0:.2%}'.format(f)],
         ['texts','{0:.2%}'.format(g),'{0:.2%}'.format(h)]]

print(tabulate(table, headers='firstrow', tablefmt='fancy_grid'))<jupyter_output>╒═════════╤════════════╤════════╕
│ unit    │ ultimate   │ surf   │
╞═════════╪════════════╪════════╡
│ cost    │ 73.89%     │ 93.69% │
├─────────┼────────────┼────────┤
│ minutes │ 19.75%     │ 83.48% │
├─────────┼────────────┼────────┤
│ data    │ 73.89%     │ 89.19% │
├─────────┼────────────┼────────┤
│ texts   │ 0.64%      │ 63.96% │
╘═════════╧════════════╧════════╛<jupyter_text>Conclusions - Of users that have the Ultimate plan, **73.89\%** spend more than the **\$70** base charge.    - Of those users going over their base charge:         - **100\%** of them went over their data limit.         - **27\%** of them went over their minutes limit.         - **1\%** of them went over their text message limit.- Of users on the Surf plan, **93.69\%** spend more than the **\$20** base charge.    - Of those users going over their base charge:         - **95\%** of them went over their data limit.         - **89\%** of them went over their minutes limit.         - **68\%** of them went over their text message limit.         Based on this, it seems as though neither plan provides enough data for most customers, but the Ultimate plan provides more than enough minutes and texts for most customers.  Outliers  Boxplots<jupyter_code>#creating boxplots to look at outliers
fig3 = plt.figure(figsize=(12,8),tight_layout=True)
gs = fig3.add_gridspec(3, 2)
f3_ax1 = fig3.add_subplot(gs[0, 0])
sns.boxplot(ax=f3_ax1, x='number_of_calls', data= user_totals_df)
f3_ax2 = fig3.add_subplot(gs[0, 1])
sns.boxplot(ax=f3_ax2, x='total_mins', data= user_totals_df)
f3_ax3 = fig3.add_subplot(gs[1, 0])
sns.boxplot(ax=f3_ax3, x='data_used', data= user_totals_df)
f3_ax4 = fig3.add_subplot(gs[1, 1])
sns.boxplot(ax=f3_ax4, x='messages_sent', data= user_totals_df)
f3_ax5 = fig3.add_subplot(gs[2, :])
sns.boxplot(ax=f3_ax5, x='cost', data= user_totals_df)
plt.show()<jupyter_output><empty_output><jupyter_text>Conclusions Looking at the boxplots, outliers occur when:- `number_of_calls` is above ~650- `total_mins` is above ~6000 - `data_used` is above ~240000 mbs- `messages_sent` is above ~550- `cost` is above ~\$2000  Removing Outliers<jupyter_code>#creating an array of the z-scores for all non bounded variables
user_totals_z_score = zscore(np.array(user_totals_df[['number_of_calls','total_mins','data_used','messages_sent','cost']]))
#removing all observations with a z-score > 3 for any of the variables
user_totals_trimmed = user_totals_df[(abs(user_totals_z_score) < 3).all(axis=1)]
user_totals_trimmed.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
Int64Index: 470 entries, 0 to 489
Data columns (total 7 columns):
 #   Column           Non-Null Count  Dtype  
---  ------           --------------  -----  
 0   user_id          470 non-null    int64  
 1   number_of_calls  470 non-null    float64
 2   total_mins       470 non-null    float64
 3   data_used        470 non-null    float64
 4   messages_sent    470 non-null    float64
 5   plan             470 non-null    object 
 6   cost             470 non-null    float64
dtypes: float64(5), int64(1), object(1)
memory usage: 29.4+ KB<jupyter_text>Conclusions I used z-scores to remove observations with an outlier value on one of the non-bounded variables. Any observation with a z-score greater than 3 was removed from the dataset. A z-score greater than 3 is a common way to detect outliers as it implies that the observation was at least 3 standard deviations away from the mean. This ended up removing 20 observations.  Test The Hypothesis  Does the Average Revenue from Users of the Ultimate and Surf Plans Differ?<jupyter_code>alpha = 0.05

results = st.ttest_ind(user_totals_trimmed[user_totals_trimmed['plan'] == 'surf']['cost'],user_totals_trimmed[user_totals_trimmed['plan'] == 'ultimate']['cost'], equal_var = False)
print('Null Hypothesis: The Surf plan and the Ultimate plan produce the same revenue')
print('Alternative Hypothesis: The Surf plan and the Ultimate plan do not produce the same revenue')
print('p-value: ', results.pvalue)

if results.pvalue < alpha:
    print("We reject the null hypothesis")
else:
    print("We can't reject the null hypothesis")<jupyter_output>Null Hypothesis: The Surf plan and the Ultimate plan produce the same revenue
Alternative Hypothesis: The Surf plan and the Ultimate plan do not produce the same revenue
p-value:  2.1335150704854422e-10
We reject the null hypothesis<jupyter_text>Conclusions Running the t-test lets us know that Megaline recieves statistically different revenue from the Surf plan than the Ultimate plan. Since, we saw above that the surf plan had higher mean costs, we know that the difference is due to the Surf plan having statistically higher revenue than the Ultimate plan. Additonally, since we saw above that people on the Surf plan use less minutes and data, while also texting less, we can say it is the more profitable plan.   Does the Average Revenue from Users of from NY-NJ and other reigons Differ?  Add State To Dataset<jupyter_code>ny_nj_df = pd.concat([users_df['user_id'],users_df['city'].str.split(',', expand=True)[1].str.split(' ', expand=True)[1].str.split('-',expand=True)], axis=1)

def is_ny_nj(row):
    for i in range(4):
        if row[i] == 'NY' or row[i] == 'NJ':
            return True
        else:
            continue
    return False
ny_nj_df['is_ny_nj'] = ny_nj_df.apply(lambda row: is_ny_nj(row), axis=1)

user_totals_df_ny_nj = user_totals_df.merge(ny_nj_df[['user_id','is_ny_nj']],on='user_id',how='inner')<jupyter_output><empty_output><jupyter_text>Conclusions In this section I split the city variable to isolate the states listed for each individual. I then checked if New York or New Jersey were listed as one of the states for an individual and created a variable that was True if they lived in New York or New Jersey and false otherwise.   Run T-Test<jupyter_code>results_1 = st.ttest_ind(user_totals_df_ny_nj[user_totals_df_ny_nj['is_ny_nj'] == True]['cost'],user_totals_df_ny_nj[user_totals_df_ny_nj['is_ny_nj'] == False]['cost'])
print('Null Hypothesis: New York and New Jersey produce the same revenue as other states in the US')
print('Alternative Hypothesis: New York and New Jersey produce different revenue as other states in the US')
print('p-value: ', results_1.pvalue)

if results_1.pvalue < alpha:
    print("We reject the null hypothesis")
else:
    print("We can't reject the null hypothesis")<jupyter_output>Null Hypothesis: New York and New Jersey produce the same revenue as other states in the US
Alternative Hypothesis: New York and New Jersey produce different revenue as other states in the US
p-value:  0.3182607881545029
We can't reject the null hypothesis
{'fraction_non_alphanumeric': 0.12383586156411404, 'fraction_numerical': 0.028834031477489936, 'mean_word_length': 4.49865831842576, 'pattern_counts': {'":': 4, '<': 104, '<?xml version=': 0, '>': 116, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '26', 'dir': 'jupyter-structured-clean-dedup', 'id': '205052', 'license': 'MIT', 'n_tokens_mistral': 8350, 'n_tokens_neox': 7689, 'n_words': 2426, 'path': 'statistical_data_analysis/statistical_data_analysis.ipynb', 'repo_name': 'bennoah1216/Practicum-Projects', 'size': '24589'}