Open In Colab

Project Final Report#

Due Date: See course schedule

Purpose#

The final report is your complete analysis notebook demonstrating mastery of machine learning applied to a chemical engineering problem. This is your opportunity to showcase everything you’ve learned in the course.

! curl -LsSf https://astral.sh/uv/install.sh | sh && \
  uv pip install -q --system "s26-06642 @ git+https://github.com/jkitchin/s26-06642.git"
from pycse.colab import pdf
downloading uv 0.10.3 x86_64-unknown-linux-gnu
no checksums to verify
installing to /home/runner/.local/bin
  uv
  uvx
everything's installed!

Report Structure#

Your report should follow this structure (approximate page lengths for guidance):

  1. Introduction (1-2 pages)

  2. Data (1-2 pages)

  3. Methods (2-3 pages)

  4. Results (2-3 pages)

  5. Discussion (1-2 pages)

  6. Conclusions (0.5 page)


1. Introduction#

Include:

  • Problem motivation: Why does this problem matter?

  • Background and prior work: What has been done before?

  • Objectives: What specific questions are you answering?

Write your introduction here


2. Data#

Include:

  • Data source and collection method

  • Feature descriptions (what each column means)

  • Exploratory data analysis with visualizations

  • Preprocessing steps (scaling, encoding, missing values, etc.)

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error

# Load your data
# df = pd.read_csv('your_data.csv')
# Data overview
# df.head()
# Summary statistics
# df.describe()
# EDA visualizations
# Add distribution plots, correlation heatmaps, scatter plots, etc.

Describe your data and preprocessing steps


3. Methods#

Include:

  • Model selection rationale: Why did you choose these methods?

  • Hyperparameter tuning approach

  • Validation strategy (train/test split, cross-validation)

# Prepare data for modeling
# X = df[feature_columns]
# y = df[target_column]
# X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Model training
# Try multiple models and compare
# Hyperparameter tuning
# from sklearn.model_selection import GridSearchCV

Explain your methodology choices


4. Results#

Include:

  • Model performance metrics (with appropriate metrics for your problem)

  • Comparison of different methods

  • Feature importance or model interpretability analysis

  • Uncertainty quantification (if applicable)

# Performance metrics
# y_pred = model.predict(X_test)
# print(f"R² Score: {r2_score(y_test, y_pred):.4f}")
# print(f"RMSE: {np.sqrt(mean_squared_error(y_test, y_pred)):.4f}")
# print(f"MAE: {mean_absolute_error(y_test, y_pred):.4f}")
# Predictions vs actual plot
# plt.figure(figsize=(8, 6))
# plt.scatter(y_test, y_pred, alpha=0.5)
# plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
# plt.xlabel('Actual')
# plt.ylabel('Predicted')
# plt.title('Predictions vs Actual')
# plt.tight_layout()
# Model comparison table
# Create a DataFrame comparing different models
# Feature importance
# import shap
# or use model.feature_importances_ for tree-based models

Present and interpret your results


5. Discussion#

Include:

  • Key findings: What did you learn?

  • Physical/chemical interpretation: Do the results make sense?

  • Limitations: What are the weaknesses of your approach?

  • Future work: What would you do differently or next?

Write your discussion here


6. Conclusions#

Summarize your main contributions and findings in a few sentences.

Write your conclusions here


What Success Looks Like#

A successful final report will demonstrate:

Criterion

Expectation

Problem Formulation

Clear, relevant, well-motivated problem

Data Analysis

Thorough EDA with informative visualizations

Methodology

Appropriate methods with proper validation

Results

Clear presentation with proper metrics

Interpretation

Domain insights and meaningful conclusions

Communication

Clear writing, well-organized, good visualizations

Code Quality

Clean, documented, reproducible code

Excellent Reports Will Also Have#

  • Multiple models compared fairly

  • Thoughtful hyperparameter tuning

  • Feature importance or SHAP analysis

  • Uncertainty quantification

  • Physical interpretation of results

  • Honest discussion of limitations

Red Flags (things to avoid)#

  • Code that doesn’t run

  • No train/test split (data leakage)

  • Only accuracy reported for imbalanced classification

  • No visualizations

  • Results that don’t make physical sense (and aren’t discussed)

  • Copy-pasted code without understanding

  • Missing sections


Submission#

Run the cell below to generate a PDF for submission.

pdf("project-report.pdf")
---------------------------------------------------------------------------
ConnectionRefusedError                    Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/urllib3/connection.py:204, in HTTPConnection._new_conn(self)
    203 try:
--> 204     sock = connection.create_connection(
    205         (self._dns_host, self.port),
    206         self.timeout,
    207         source_address=self.source_address,
    208         socket_options=self.socket_options,
    209     )
    210 except socket.gaierror as e:

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/urllib3/util/connection.py:85, in create_connection(address, timeout, source_address, socket_options)
     84 try:
---> 85     raise err
     86 finally:
     87     # Break explicitly a reference cycle

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/urllib3/util/connection.py:73, in create_connection(address, timeout, source_address, socket_options)
     72     sock.bind(source_address)
---> 73 sock.connect(sa)
     74 # Break explicitly a reference cycle

ConnectionRefusedError: [Errno 111] Connection refused

The above exception was the direct cause of the following exception:

NewConnectionError                        Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/urllib3/connectionpool.py:787, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)
    786 # Make the request on the HTTPConnection object
--> 787 response = self._make_request(
    788     conn,
    789     method,
    790     url,
    791     timeout=timeout_obj,
    792     body=body,
    793     headers=headers,
    794     chunked=chunked,
    795     retries=retries,
    796     response_conn=response_conn,
    797     preload_content=preload_content,
    798     decode_content=decode_content,
    799     **response_kw,
    800 )
    802 # Everything went great!

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/urllib3/connectionpool.py:493, in HTTPConnectionPool._make_request(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)
    492 try:
--> 493     conn.request(
    494         method,
    495         url,
    496         body=body,
    497         headers=headers,
    498         chunked=chunked,
    499         preload_content=preload_content,
    500         decode_content=decode_content,
    501         enforce_content_length=enforce_content_length,
    502     )
    504 # We are swallowing BrokenPipeError (errno.EPIPE) since the server is
    505 # legitimately able to close the connection after sending a valid response.
    506 # With this behaviour, the received response is still readable.

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/urllib3/connection.py:500, in HTTPConnection.request(self, method, url, body, headers, chunked, preload_content, decode_content, enforce_content_length)
    499     self.putheader(header, value)
--> 500 self.endheaders()
    502 # If we're given a body we start sending that in chunks.

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/http/client.py:1298, in HTTPConnection.endheaders(self, message_body, encode_chunked)
   1297     raise CannotSendHeader()
-> 1298 self._send_output(message_body, encode_chunked=encode_chunked)

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/http/client.py:1058, in HTTPConnection._send_output(self, message_body, encode_chunked)
   1057 del self._buffer[:]
-> 1058 self.send(msg)
   1060 if message_body is not None:
   1061 
   1062     # create a consistent interface to message_body

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/http/client.py:996, in HTTPConnection.send(self, data)
    995 if self.auto_open:
--> 996     self.connect()
    997 else:

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/urllib3/connection.py:331, in HTTPConnection.connect(self)
    330 def connect(self) -> None:
--> 331     self.sock = self._new_conn()
    332     if self._tunnel_host:
    333         # If we're tunneling it means we're connected to our proxy.

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/urllib3/connection.py:219, in HTTPConnection._new_conn(self)
    218 except OSError as e:
--> 219     raise NewConnectionError(
    220         self, f"Failed to establish a new connection: {e}"
    221     ) from e
    223 sys.audit("http.client.connect", self, self.host, self.port)

NewConnectionError: HTTPConnection(host='10.1.0.65', port=9000): Failed to establish a new connection: [Errno 111] Connection refused

The above exception was the direct cause of the following exception:

MaxRetryError                             Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/requests/adapters.py:644, in HTTPAdapter.send(self, request, stream, timeout, verify, cert, proxies)
    643 try:
--> 644     resp = conn.urlopen(
    645         method=request.method,
    646         url=url,
    647         body=request.body,
    648         headers=request.headers,
    649         redirect=False,
    650         assert_same_host=False,
    651         preload_content=False,
    652         decode_content=False,
    653         retries=self.max_retries,
    654         timeout=timeout,
    655         chunked=chunked,
    656     )
    658 except (ProtocolError, OSError) as err:

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/urllib3/connectionpool.py:841, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)
    839     new_e = ProtocolError("Connection aborted.", new_e)
--> 841 retries = retries.increment(
    842     method, url, error=new_e, _pool=self, _stacktrace=sys.exc_info()[2]
    843 )
    844 retries.sleep()

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/urllib3/util/retry.py:535, in Retry.increment(self, method, url, response, error, _pool, _stacktrace)
    534     reason = error or ResponseError(cause)
--> 535     raise MaxRetryError(_pool, url, reason) from reason  # type: ignore[arg-type]
    537 log.debug("Incremented Retry for (url='%s'): %r", url, new_retry)

MaxRetryError: HTTPConnectionPool(host='10.1.0.65', port=9000): Max retries exceeded with url: /api/sessions (Caused by NewConnectionError("HTTPConnection(host='10.1.0.65', port=9000): Failed to establish a new connection: [Errno 111] Connection refused"))

During handling of the above exception, another exception occurred:

ConnectionError                           Traceback (most recent call last)
Cell In[13], line 1
----> 1 pdf("project-report.pdf")

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/pycse/colab.py:287, in pdf(line)
    285     delay = 10000
    286 plotly = "-p" in args
--> 287 pdf_from_html(pdf, verbose, plotly, delay)

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/pycse/colab.py:122, in pdf_from_html(pdf, verbose, plotly, javascript_delay)
    119 if verbose:
    120     print("PDF via wkhtmltopdf")
--> 122 fname, fid = current_notebook()
    123 ipynb = notebook_string(fid)
    125 if plotly:

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/pycse/colab.py:86, in current_notebook()
     84 ip = gethostbyname(gethostname())
     85 url = f"http://{ip}:9000/api/sessions"
---> 86 d = requests.get(url).json()[0]
     87 fid = d["path"].split("=")[1]
     88 fname = d["name"]

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/requests/api.py:73, in get(url, params, **kwargs)
     62 def get(url, params=None, **kwargs):
     63     r"""Sends a GET request.
     64 
     65     :param url: URL for the new :class:`Request` object.
   (...)     70     :rtype: requests.Response
     71     """
---> 73     return request("get", url, params=params, **kwargs)

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/requests/api.py:59, in request(method, url, **kwargs)
     55 # By using the 'with' statement we are sure the session is closed, thus we
     56 # avoid leaving sockets open which can trigger a ResourceWarning in some
     57 # cases, and look like a memory leak in others.
     58 with sessions.Session() as session:
---> 59     return session.request(method=method, url=url, **kwargs)

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/requests/sessions.py:589, in Session.request(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)
    584 send_kwargs = {
    585     "timeout": timeout,
    586     "allow_redirects": allow_redirects,
    587 }
    588 send_kwargs.update(settings)
--> 589 resp = self.send(prep, **send_kwargs)
    591 return resp

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/requests/sessions.py:703, in Session.send(self, request, **kwargs)
    700 start = preferred_clock()
    702 # Send the request
--> 703 r = adapter.send(request, **kwargs)
    705 # Total elapsed time of the request (approximately)
    706 elapsed = preferred_clock() - start

File /opt/hostedtoolcache/Python/3.11.14/x64/lib/python3.11/site-packages/requests/adapters.py:677, in HTTPAdapter.send(self, request, stream, timeout, verify, cert, proxies)
    673     if isinstance(e.reason, _SSLError):
    674         # This branch is for urllib3 v1.22 and later.
    675         raise SSLError(e, request=request)
--> 677     raise ConnectionError(e, request=request)
    679 except ClosedPoolError as e:
    680     raise ConnectionError(e, request=request)

ConnectionError: HTTPConnectionPool(host='10.1.0.65', port=9000): Max retries exceeded with url: /api/sessions (Caused by NewConnectionError("HTTPConnection(host='10.1.0.65', port=9000): Failed to establish a new connection: [Errno 111] Connection refused"))