<jupyter_start><jupyter_text>Homework 11: Regression Inference **Reading**: * [Inference for Regression](https://www.inferentialthinking.com/chapters/16/Inference_for_Regression.html) Please complete this notebook by filling in the cells provided.Directly sharing answers is not okay, but discussing problems with the course staff or with other students is encouraged. Refer to the policies page to learn more about how to learn cooperatively.For all problems that you must write our explanations and sentences for, you **must** provide your answer in the designated space. Moreover, throughout this homework and all future ones, please be sure to not re-assign variables throughout the notebook! For example, if you use `max_temperature` in your answer to one question, do not reassign it later on.<jupyter_code># Don't change this cell; just run it. 

import numpy as np
from datascience import *

# These lines do some fancy plotting magic.
import matplotlib
%matplotlib inline
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')
import warnings
warnings.simplefilter('ignore', FutureWarning)
from matplotlib import patches
from ipywidgets import interact, interactive, fixed
import ipywidgets as widgets<jupyter_output><empty_output><jupyter_text>Regression Inference for the NFL Draft In this homework, we will be analyzing the relationship between draft position and success in the NFL. The NFL draft is an annual event in which every NFL team takes turns choosing players that they will add to their team. There are around 200 selections, called "picks" made every year, although this number has changed over the years.The `nfl` table has five columns, the name of the `Player`, the `Salary` that player made for the 2019 season, the year that player was drafted (`Year Drafted`), the number of the draft pick that was used when the player was drafted (`Pick Number`), and the `Position` in football that player plays.Each row in `nfl` corresponds to one player who played in the **2019 season**.<jupyter_code># Just run this cell!
nfl = Table.read_table("nfl.csv")
nfl.show(5)<jupyter_output><empty_output><jupyter_text>Question 1Add a column to the table called `Career Length` that corresponds to how long a player has been in the NFL to the `nfl` table. `Career Length` is from when they were drafted to this year, 2020. So, if a player was drafted in 2015, their career length is 5:$$2020-2015=5$$<!--BEGIN QUESTIONname: q1_1manual: false--><jupyter_code>nfl = ...
nfl.show(5)
grader.check("q1_1")<jupyter_output><empty_output><jupyter_text>As usual, let's investigate our data visually before analyzing it numerically. The first relationship we will analyze is the relationship between a player's `Pick Number` and their `Career Length`. Run the following cell to see a scatter diagram with the line of best fit already plotted for you.<jupyter_code># Just run this cell
nfl.scatter("Pick Number", "Career Length", fit_line=True)<jupyter_output><empty_output><jupyter_text>Question 2Use the functions given to assign the correlation between `Pick Number` and `Career Length` to `pick_length_correlation`. `correlation` takes in three arguments, a table `tbl` and the labels of the columns you are finding the correlation between, `col1` and `col2`.<!--BEGIN QUESTIONname: q1_2manual: false--><jupyter_code>def standard_units(arr):
    return (arr- np.mean(arr)) / np.std(arr)

def correlation(tbl, col1, col2):
    r = np.mean(standard_units(tbl.column(col1)) * standard_units(tbl.column(col2)))
    return r

pick_length_correlation = ...
pick_length_correlation
grader.check("q1_2")<jupyter_output><empty_output><jupyter_text>We can see that there is a negative association between `Pick Number` and `Career Length`! If in the sample, we found a linear relation between the two variables, would the same be true for the population? Would it be exactly the same linear relation? Could we predict the response of a new individual who is not in our sample?  Question 3Evan thinks that the slope of the true line of best fit for `Pick Number` and `Career Length` is not zero: that is, there is some correlation/association between `Pick Number` and `Career Length`. To test this claim, we can run a hypothesis test! Define the null and alternative hypothesis for this test.<!--BEGIN QUESTIONname: q1_3manual: true--> _Type your answer here, replacing this text._  Question 4Saurav says that instead of finding the slope for each resample, we can find the correlation instead, and that we will get the same result. Why is he correct? What is the relationship between slope and correlation?<!--BEGIN QUESTIONname: q1_4manual: true--> _Type your answer here, replacing this text._  Question 5Define the function `one_resample_r` that performs a bootstrap and finds the correlation between `Pick Number` and `Career Length` in the resample. `one_resample_r` should take three arguments, a table `tbl` and the labels of the columns you are finding the correlation between, `col1` and `col2`.<!--BEGIN QUESTIONname: q1_5manual: false--><jupyter_code>def one_resample_r(tbl, col1, col2):
    ...

# Don't change this line below!
one_resample = one_resample_r(nfl, "Pick Number", "Career Length")
one_resample
grader.check("q1_5")<jupyter_output><empty_output><jupyter_text>Question 6Generate 1000 bootstrapped correlations for `Pick Number` and `Career Length`, store your results in the array `resampled_correlations_pc`, and plot a histogram of your results.<!--BEGIN QUESTIONname: q1_6manual: true--><jupyter_code>resampled_correlations_pc = ...
...
Table().with_column("Resampled Correlations, Pick Number vs Career Length", resampled_correlations_pc).hist()<jupyter_output><empty_output><jupyter_text>Question 7Calculate a 95% confidence interval for the resampled correlations and assign either `True` or `False` to `reject` if we can reject the null hypothesis or if we cannot reject the null hypothesis using a 5% p-value cutoff.<!--BEGIN QUESTIONname: q1_7manual: false--><jupyter_code>lower_bound_pc = ...
upper_bound_pc = ...
reject = ...

# Don't change this!
print(f"95% CI: [{lower_bound_pc}, {upper_bound_pc}] , Reject the null: {reject}")
grader.check("q1_7")<jupyter_output><empty_output><jupyter_text>Now let's investigate the relationship between `Pick Number` and `Salary`. As usual, let's inspect our data visually first. A line of best fit is plotted for you.<jupyter_code># Just run this cell!
nfl.scatter("Pick Number", "Salary", fit_line=True)<jupyter_output><empty_output><jupyter_text>Question 8Using the function `correlation`, find the correlation between `Pick Number` and `Salary` and assign it to `pick_salary_correlation`.<!--BEGIN QUESTIONname: q1_8manual: false--><jupyter_code>pick_salary_correlation = ...
pick_salary_correlation
grader.check("q1_8")<jupyter_output><empty_output><jupyter_text>We can see that there is a negative association between `Pick Number` and `Salary`!   Question 9Once again, Evan thinks that the slope of the true line of best fit for `Pick Number` and `Salary` is not zero: that is, there is some correlation/association between `Pick Number` and `Salary`. To test this claim, we can run a hypothesis test! Define the null and alternative hypothesis for this test.<!--BEGIN QUESTIONname: q1_9manual: true--> _Type your answer here, replacing this text._  Question 10Generate 1000 bootstrapped correlations for `Pick Number` and `Salary`, append them to the array `resampled_correlations_salary`, and then plot a histogram of your results.<!--BEGIN QUESTIONname: q1_10manual: true--><jupyter_code>resampled_correlations_salary = ...
...
Table().with_column("Resampled Correlations for Salary", resampled_correlations_salary).hist()<jupyter_output><empty_output><jupyter_text>Question 11Calculate a 95% confidence interval for the resampled correlations and assign either `True` or `False` to `reject_sal` if we can reject the null hypothesis or if we cannot reject the null hypothesis using a 5% p-value cutoff.<!--BEGIN QUESTIONname: q1_11manual: false--><jupyter_code>lower_bound_sal = ...
upper_bound_sal = ...
reject_sal = ...

# Don't change this!
print(f"95% CI: [{lower_bound_sal}, {upper_bound_sal}], Reject the null: {reject_sal}")
grader.check("q1_11")<jupyter_output><empty_output><jupyter_text>Analyzing Residuals Next, Evan wants to predict his Career Length and Salary based on his Pick Number. To understand what his Career Length and Salary might be, Evan wants to generate confidence intervals of possible values for both career length and salary. First, let's investigate how effective our predictions for career length and salary based on pick number are.  Question 12Calculate the slope and intercept for the line of best fit for `Pick Number` vs `Career Length` and for `Pick Number` vs `Salary`. Assign these values to `career_length_slope`, `career_length_intercept`, `salary_slope`, and `salary_intercept` respectively. The function `parameters` returns a two-item array containing the slope and intercept of a linear regression line.*Hint 1: Use the `parameters` function with the arguments specified!**Hint 2: Remember we're predicting career length and salary **based off** a pick number. That should tell you what the `colx` and `coly` arguments you should specify when calling `parameters`.*<!--BEGIN QUESTIONname: q1_12manual: false--><jupyter_code># DON'T EDIT THE PARAMETERS FUNCTION
def parameters(tbl, colx, coly):
    x = tbl.column(colx)
    y = tbl.column(coly)
    
    r = correlation(tbl, colx, coly)
    
    x_mean = np.mean(x)
    y_mean = np.mean(y)
    x_sd = np.std(x)
    y_sd = np.std(y)
    
    slope = (y_sd / x_sd) * r
    intercept = y_mean - (slope * x_mean)
    return make_array(slope, intercept)

career_length_slope = ...
career_length_intercept = ...

salary_slope = ...
salary_intercept = ...
grader.check("q1_12")<jupyter_output><empty_output><jupyter_text>Question 13Draw a scatter plot of the residuals for each line of best fit for `Pick Number` vs `Career Length` and for `Pick Number` vs `Salary`.*Hint: We want to get the predictions for every player in the dataset**Hint 2: This question is really involved, try to follow the skeleton code!*<!--BEGIN QUESTIONname: q1_13manual: true--><jupyter_code>predicted_career_lengths = ...
predicted_salaries = ...


career_length_residuals = ...
salary_residuals = ...

nfl_with_residuals = nfl.with_columns("Career Length Residuals", career_length_residuals, "Salary Residuals", salary_residuals)

# Now generate two scatter plots!
nfl_with_residuals.scatter("Pick Number", "Career Length Residuals")
nfl_with_residuals.scatter("Pick Number", "Salary Residuals")<jupyter_output><empty_output><jupyter_text>Here's a [link](https://www.inferentialthinking.com/chapters/15/6/Numerical_Diagnostics.html) to properties of residuals in the textbook that could help out with some questions.  Question 14Based on these plots of residuals, do you think linear regression is a good model for `Pick Number` vs `Career Length` and for `Pick Number` vs `Salary`? Explain for both.<!--BEGIN QUESTIONname: q1_14manual: true--> _Type your answer here, replacing this text._  Question 15Assign `career_length_residual_corr` and `salary_residual_corr` to either 1, 2 or 3 corresponding to whether or not the correlation between `Pick Number` and `Career Length Residuals` is positive, zero, or negative, and to whether or not the correlation between `Pick Number` and `Salary Residuals` is positive, zero, or negative respectively.1. Positive2. Zero3. Negative<!--BEGIN QUESTIONname: q1_15manual: false--><jupyter_code>career_length_residual_corr = ...
salary_residual_corr = ...
grader.check("q1_15")<jupyter_output><empty_output><jupyter_text>It looks like the largest residuals are positive residuals, so let's investigate those more closely.  Question 16Let's investigate where our regression line is making errors. Using the `nfl_with_residuals` table, assign `greatest_career_length_residual` to the string that is the name of the player with the largest positive residual for `Pick Number` vs `Career Length`.<!--BEGIN QUESTIONname: q1_16manual: false--><jupyter_code>greatest_career_length_residual = ...
greatest_career_length_residual
grader.check("q1_16")<jupyter_output><empty_output><jupyter_text>Now let's investigate the residuals for salary. Run the cell below to see the players with the largest residuals for `Pick Number` vs `Salary`.<jupyter_code># Just run this cell!
nfl_with_residuals.sort("Salary Residuals", descending=True).take(np.arange(10)).drop(2,6)<jupyter_output><empty_output><jupyter_text>Question 17What patterns do you notice with these large residuals for salary? How could this affect our analysis?<!--BEGIN QUESTIONname: q1_17manual: true--> _Type your answer here, replacing this text._  Prediction Intervals Now, Evan wants to predict his career length based on his specific pick number, which is 169. Instead of using the best fit line generated from the sample, Evan wants to generate an interval for his predicted career length.  Question 18Define the function `one_resample_prediction` that generates a bootstrapped sample from the `tbl` argument, calculates the line of best fit for `ycol` vs `xcol` for that resample, and predicts a value based on `xvalue`.*Hint: Remember you defined the `parameters` function earlier*<!--BEGIN QUESTIONname: q1_18manual: false--><jupyter_code>def one_resample_prediction(tbl, colx, coly, xvalue):
    ...

evans_career_length_pred = one_resample_prediction(nfl, "Pick Number", "Career Length", 169)
evans_career_length_pred
grader.check("q1_18")<jupyter_output><empty_output><jupyter_text>Question 19Assign `resampled_predictions` to be an array that will contain 1000 resampled predictions for Evan's career length based on his pick number, and then generate a histogram of it.<!--BEGIN QUESTIONname: q1_19manual: true--><jupyter_code>resampled_predictions = ...

...

# Don't change/delete the code below in this cell
Table().with_column("Resampled Career Length Predictions", resampled_predictions).hist()<jupyter_output><empty_output><jupyter_text>Question 20Using `resampled_predictions` from Question 19, generate a 99% confidence interval for Evan's predicted career lengths.<!--BEGIN QUESTIONname: q1_20manual: false--><jupyter_code>lower_bound_evan = ...
upper_bound_evan = ...

# Don't delete/modify the code below in this cell
print(f"99% CI: [{lower_bound_evan}, {upper_bound_evan}]")
grader.check("q1_20")<jupyter_output><empty_output><jupyter_text>Run the following cell to see a few bootstrapped regression lines, and the predictions they make for a career length from a pick number of 169.<jupyter_code># Just run this cell! 
# You don't need to understand all of what it is doing but you should recognize a lot of the code!
lines = Table(['slope','intercept'])
x=169
for i in np.arange(20):
    resamp = nfl.sample(with_replacement=True)
    resample_pars = parameters(resamp, "Pick Number", "Career Length") 
    slope = resample_pars.item(0)
    intercept = resample_pars.item(1)
    lines.append([slope, intercept])
    
lines['prediction at x='+str(x)] = lines.column('slope')*x + lines.column('intercept')
xlims = [min(nfl.column("Pick Number")), max(nfl.column("Pick Number"))]
left = xlims[0]*lines[0] + lines[1]
right = xlims[1]*lines[0] + lines[1]
fit_x = x*lines['slope'] + lines['intercept']
for i in range(20):
    plt.plot(xlims, np.array([left[i], right[i]]), lw=1)
    plt.scatter(x, fit_x[i], s=30)
plt.ylabel("Career Length");
plt.xlabel("Pick Number");
plt.title("Resampled Regression Lines");<jupyter_output><empty_output><jupyter_text>Question 21Does the Central Limit Theorem guarantee that the bootstrapped slopes or bootstrapped correlations will be normally distributed for any dataset that uses a large random sample? If you think yes, assign `True` to `clt_applies`, otherwise assign `False` to `clt_applies` if you think no. Are residuals normally distributed? If you think they are, assign `True` to `residuals_normal`, otherwise assign `False` to `residuals_normal`.<!--BEGIN QUESTIONname: q1_21manual: false--><jupyter_code>clt_applies = ...
residuals_normal = ...
grader.check("q1_21")<jupyter_output><empty_output><jupyter_text>Question 22What are some biases in this dataset that may have affected our analysis? Some questions you can ask yourself are: "is our sample a simple random sample?" or "what kind of data are we using/what variables are we dealing with: are they categorical, numerical, or both (both is something like ordinal data)?".*Hint: you might want to revisit the beginning of this assignment to reread how this data/`nfl` table was generated.*<!--BEGIN QUESTIONname: q1_22manual: true--> _Type your answer here, replacing this text._  (OPTIONAL, Out of Scope) Extending Linear Regression This following section is completely **optional**, meaning there's no code to be graded/filled in. Just run the cells/explore if you're interested.In the past few weeks you have learned one of the most powerful tools in a data scientist's arsenal: regression. At this point you may be wondering: what do we do when our data is not linear? You have learned that you shouldn't try and force models when they are bad fits: for example, if we detect heteroscedasticity in our residuals plot, we know that linear regression is a bad fit.How can we fit data that is not linear then? Let's increase our data's complexity a little: instead of linear data, let's look at data that you would naturally model with a parabola instead:<jupyter_code>def parabola(x, a=1, b=0, c=0):
    random_noise = np.random.normal(size=len(x)) * 3
    return  a*(x**2) + b*(x) + c + random_noise

size = 500
x_values = np.random.uniform(-5, 10, size=size)
y_values = parabola(x_values, a=2, b=-3, c=5)

Table().with_columns("X", x_values, "Y", y_values).scatter("X","Y", fit_line=True)<jupyter_output><empty_output><jupyter_text>You can see that our line of best fit is a poor match for this data. Let's look at the residual plot:<jupyter_code>def mse(slope, intercept):
    predicted_y = slope * x_values + intercept
    errors = y_values - predicted_y
    return np.mean(errors**2)


slope_and_intercept = minimize(mse, smooth=True)
predicted_y = slope_and_intercept.item(0) * x_values + slope_and_intercept.item(1)
residuals = y_values - predicted_y

Table().with_columns("X", x_values, "Residuals",residuals).scatter("X", "Residuals")<jupyter_output><empty_output><jupyter_text>Our residuals clearly have a pattern, confirming that linear regression is a bad fit for this data! In fact, our residuals actually look like our original data. Linear regression generates a line that minimizes mean squared error. Using the `minimize` function on the `mse` function does all the work of finding values for us! Can we use `minimize` for more complicated models? Yes! In future data science classes, you will learn how to find these values yourself using the mathematical fields of Linear Algebra (note that it involves lines!) and calculus! Let's take a look at the equation for a line:$$y = ax +b$$There are two parameters here that we can change: $a$, which is the slope, and $b$, which is the intercept.How about the equation for a parabola?$$y = ax^2 + bx + c$$Now there are three parameters, $a,b,c$.Let's change our mse function to incorporate these three parameters!<jupyter_code>def mse_parabola(a, b, c):
    predicted_y = a * (x_values**2) + b * (x_values) + c
    errors = y_values - predicted_y
    return np.mean(errors**2)<jupyter_output><empty_output><jupyter_text>The function still returns the mean squared error of our predicted curve, just our curve is now a parabola with the parameters `a`, `b`, and `c`. Let's try and minimize this function!<jupyter_code>params = minimize(mse_parabola, smooth=True)
a = params.item(0)
b = params.item(1)
c = params.item(2)
a, b, c<jupyter_output><empty_output><jupyter_text>Let's plot our new curve with these values!<jupyter_code>x_values_range = np.linspace(-5, 10, 1000)
predicted_y = a * (x_values_range**2) + b * (x_values_range) + c

Table().with_columns("X", x_values, "Y", y_values).scatter("X", "Y")
plt.plot(x_values_range, predicted_y, color='gold', markersize=1);<jupyter_output><empty_output><jupyter_text>Our curve looks like a much better fit now! Let's double check the residuals plot to be sure.<jupyter_code>residuals = y_values - (a * (x_values**2) + b * (x_values) + c)
Table().with_columns("X", x_values, "Residuals", residuals).scatter("X", "Residuals")<jupyter_output><empty_output><jupyter_text>A formless cloud, excellent! What else can the method of least squares do?Can we predict a single variable based on the values of two other variables? Right now, we don't have a way of doing that. If you look at the previous example, you could say that the $x^2$ term is actually a second variable.Let's generate a dataset to work with. We are going to try and predict `z` based on `x` and `y`.<jupyter_code>x_values_range = np.linspace(-5, 10, 1000)

x = 0.5 * np.random.uniform(-5, 10, size=size) + 3
y = np.random.uniform(-5, 10, size=size) - 1
z = 3*x  + (-2*y) -4 + np.random.normal(size=size)

data = Table().with_columns("x", x, "y", y, "z", z)
data.scatter("x", "y")
data.scatter("x", "z")
data.scatter("y", "z")<jupyter_output><empty_output><jupyter_text>We can see that `x` and `y` would both be very helpful to predict `z` by themselves! However, if we combined them we could predict `z` even better. Since our goal is to minimize mean squared error, let's find the mean squared error of the models that only use `x` and `y` by themselves (using an intercept).<jupyter_code>from scipy import stats
def su(x):
    return (x-np.mean(x)) / np.std(x)
def r(x, y):
    return np.mean(su(x) * su(y))

def mse_x(slope, intercept):
    predicted_z = slope * x + intercept
    errors = z - predicted_z
    return np.mean(errors**2)

def mse_y(slope, intercept):
    predicted_z = slope * y + intercept
    errors = z - predicted_z
    return np.mean(errors**2)


slope_and_intercept_x = minimize(mse_x, smooth=True)
predicted_z_x = slope_and_intercept_x.item(0) * x + slope_and_intercept_x.item(1)
residuals_x = z - predicted_z_x

Table().with_columns("X", x, "Residuals for X Model", residuals_x).scatter("X", "Residuals for X Model")

slope_and_intercept_y = minimize(mse_y, smooth=True)
predicted_z_y = slope_and_intercept_y.item(0) * y + slope_and_intercept_y.item(1)
residuals_y = z - predicted_z_y

Table().with_columns("Y", y, "Residuals for Y Model", residuals_y).scatter("Y", "Residuals for Y Model")<jupyter_output><empty_output><jupyter_text>Both of the residual plots show no trend, so using these `x` or `y` by themselves would work, but how good are these models? Let's calculate their actual mse.<jupyter_code>x_only_mse = mse_x(slope_and_intercept_x.item(0), slope_and_intercept_x.item(1))
y_only_mse = mse_y(slope_and_intercept_y.item(0), slope_and_intercept_y.item(1))

print(f"X only model MSE: {x_only_mse}, Y only model MSE: {y_only_mse}")<jupyter_output><empty_output><jupyter_text>Looks like the y only model has lower MSE, so we should try and use that if we can only use `x` or `y`. Instead, let's try to build a model that is a combination of `x`, `y` and an intercept `c` to predict `z`!$$z = ax + by +c$$<jupyter_code>def mse_both(a, b, c):
    predicted_z = (a * x) + (b * y) + c
    errors = z - predicted_z
    return np.mean(errors**2)

slope_and_intercept_both = minimize(mse_both, smooth=True)
predicted_z = (slope_and_intercept_both.item(0) * x) + (slope_and_intercept_both.item(1) * y) + slope_and_intercept_both.item(2)
residuals = z - predicted_z

Table().with_columns("X", x, "Residuals for Full Model", residuals).scatter("X", "Residuals for Full Model")
Table().with_columns("Y", x, "Residuals for Full Model", residuals).scatter("Y", "Residuals for Full Model")<jupyter_output><empty_output><jupyter_text>This model is also a good fit looking at the residuals with respect to both `x` and `y`! What is this model's mse?<jupyter_code>full_model_mse = mse_both(slope_and_intercept_both.item(0), slope_and_intercept_both.item(1), slope_and_intercept_both.item(2))

print(f"X only model MSE: {x_only_mse}, Y only model MSE: {y_only_mse}, Both X and Y MSE: {full_model_mse}")<jupyter_output><empty_output><jupyter_text>That MSE is much lower! We should definitely use this model instead of either the x only or y only model independently!Let's try and visualize what this model looks like with a 3D graph!<jupyter_code>import matplotlib
%matplotlib inline
import matplotlib.pyplot as plt

from mpl_toolkits.mplot3d import Axes3D
import seaborn as sns
sns.set_style("whitegrid", {'axes.grid' : False})

fig = plt.figure(figsize=(10,7));
ax = fig.add_subplot(111, projection='3d');
ax.scatter(x, y, z);
ax.set_xlabel('X');
ax.set_ylabel('Y');
ax.set_zlabel('Z');

ax.scatter(x,y,predicted_z);<jupyter_output><empty_output><jupyter_text>Once we start working in more dimensions, visualization becomes increasingly difficult and useless. Instead of predicting a line, our prediction is actually a plane of values (the red values)! ---To double-check your work, the cell below will rerun all of the autograder tests.<jupyter_code>grader.check_all()<jupyter_output><empty_output><jupyter_text>SubmissionMake sure you have run all cells in your notebook in order before running the cell below, so that all images/graphs appear in the output. The cell below will generate a zip file for you to submit. **Please save before exporting!**<jupyter_code># Save your notebook first, then run this cell to export your submission.
grader.export()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09821880968156839, 'fraction_numerical': 0.012472229800834081, 'mean_word_length': 5.619711042311661, 'pattern_counts': {'":': 0, '<': 167, '<?xml version=': 0, '>': 167, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '36', 'dir': 'jupyter-structured-clean-dedup', 'id': '181719', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 8310, 'n_tokens_neox': 7839, 'n_words': 2957, 'path': 'hw/hw11/.ipynb_checkpoints/hw11-checkpoint.ipynb', 'repo_name': 'ds-modules/Colab-demo', 'size': '25657'}