{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Predict/Classification of user's rating given review text","metadata":{}},{"cell_type":"markdown","source":"## Import Libraries","metadata":{}},{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport plotly.express as px\nimport matplotlib.pyplot as plt\nimport seaborn as sb\nimport re\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\nfrom plotly.offline import init_notebook_mode\ninit_notebook_mode(connected=True)\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-02-04T10:26:26.145264Z","iopub.execute_input":"2023-02-04T10:26:26.145679Z","iopub.status.idle":"2023-02-04T10:26:26.876101Z","shell.execute_reply.started":"2023-02-04T10:26:26.145645Z","shell.execute_reply":"2023-02-04T10:26:26.874663Z"},"_kg_hide-output":false,"_kg_hide-input":true,"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import logging\nfrom numpy import random\nimport gensim\nimport nltk\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer\nfrom sklearn.metrics import accuracy_score, confusion_matrix\nfrom nltk.corpus import stopwords\nfrom tqdm import tqdm\nimport re\nfrom bs4 import BeautifulSoup\n%matplotlib inline","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:26:26.878389Z","iopub.execute_input":"2023-02-04T10:26:26.879180Z","iopub.status.idle":"2023-02-04T10:26:27.432910Z","shell.execute_reply.started":"2023-02-04T10:26:26.879127Z","shell.execute_reply":"2023-02-04T10:26:27.431950Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Load The Dataset","metadata":{}},{"cell_type":"code","source":"df = pd.read_csv('/kaggle/input/goodreads-books-reviews-290312/goodreads_train.csv',\n                usecols=['review_text', 'rating'])","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:26:27.434180Z","iopub.execute_input":"2023-02-04T10:26:27.434823Z","iopub.status.idle":"2023-02-04T10:26:38.205567Z","shell.execute_reply.started":"2023-02-04T10:26:27.434791Z","shell.execute_reply":"2023-02-04T10:26:38.204599Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Quick Lookup","metadata":{}},{"cell_type":"code","source":"df.head()","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:26:38.208020Z","iopub.execute_input":"2023-02-04T10:26:38.208451Z","iopub.status.idle":"2023-02-04T10:26:38.224767Z","shell.execute_reply.started":"2023-02-04T10:26:38.208419Z","shell.execute_reply":"2023-02-04T10:26:38.223453Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(df['review_text'].apply(lambda x: len(x.split(' '))).sum())","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:26:38.226600Z","iopub.execute_input":"2023-02-04T10:26:38.227075Z","iopub.status.idle":"2023-02-04T10:26:46.798539Z","shell.execute_reply.started":"2023-02-04T10:26:38.227039Z","shell.execute_reply":"2023-02-04T10:26:46.797229Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(10,4))\ndf.rating.value_counts().plot(kind='bar')","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:26:46.802029Z","iopub.execute_input":"2023-02-04T10:26:46.802363Z","iopub.status.idle":"2023-02-04T10:26:47.028379Z","shell.execute_reply.started":"2023-02-04T10:26:46.802332Z","shell.execute_reply":"2023-02-04T10:26:47.027184Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The rating label is imbalanced. <br/>\nHow can I handle this? hmm... <br/>\n - Using weighted RMSE loss?\n - Using scikit-learn's `LogisticRegression` class with weighting?","metadata":{}},{"cell_type":"markdown","source":"## Text Preprocessing","metadata":{}},{"cell_type":"code","source":"stopwords = stopwords.words('english')","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:26:47.029873Z","iopub.execute_input":"2023-02-04T10:26:47.030904Z","iopub.status.idle":"2023-02-04T10:26:47.039963Z","shell.execute_reply.started":"2023-02-04T10:26:47.030869Z","shell.execute_reply":"2023-02-04T10:26:47.038762Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def preprocess_desc(text_col):\n    preprocessed_descs = []\n    url_pattern = re.compile('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+')\n    tag_pattern = re.compile('@(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+')\n    for sentence in tqdm(text_col.values):\n        if sentence is np.nan:\n            preprocessed_descs.append(sent.lower().strip())\n            continue\n        else:\n            sent = url_pattern.sub('', sentence)\n            sent = tag_pattern.sub('', sent)\n            sent = sent.replace('\\\\r', ' ')\n            sent = sent.replace('\\\\\"', ' ')\n            sent = sent.replace('\\\\n', ' ')\n            sent = re.sub('[^A-Za-z0-9]+', ' ', sent)\n            sent = ' '.join(e for e in sent.split() if e not in stopwords)\n            preprocessed_descs.append(sent.lower().strip())\n    return preprocessed_descs","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:26:47.041433Z","iopub.execute_input":"2023-02-04T10:26:47.041793Z","iopub.status.idle":"2023-02-04T10:26:47.051600Z","shell.execute_reply.started":"2023-02-04T10:26:47.041755Z","shell.execute_reply":"2023-02-04T10:26:47.050451Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = df.assign(pre_text = preprocess_desc(df['review_text']))","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:26:47.053188Z","iopub.execute_input":"2023-02-04T10:26:47.053548Z","iopub.status.idle":"2023-02-04T10:34:54.047272Z","shell.execute_reply.started":"2023-02-04T10:26:47.053499Z","shell.execute_reply":"2023-02-04T10:34:54.046164Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(df['pre_text'].apply(lambda x: len(x.split(' '))).sum())","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:34:54.052827Z","iopub.execute_input":"2023-02-04T10:34:54.053189Z","iopub.status.idle":"2023-02-04T10:34:59.319825Z","shell.execute_reply.started":"2023-02-04T10:34:54.053159Z","shell.execute_reply":"2023-02-04T10:34:59.318699Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.head()","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:34:59.321145Z","iopub.execute_input":"2023-02-04T10:34:59.321895Z","iopub.status.idle":"2023-02-04T10:34:59.334471Z","shell.execute_reply.started":"2023-02-04T10:34:59.321857Z","shell.execute_reply":"2023-02-04T10:34:59.333008Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Train/Test Split","metadata":{}},{"cell_type":"code","source":"# Split the data into training and testing sets\nX_train, X_test, y_train, y_test = train_test_split(df['pre_text'], df['rating'], test_size=0.2, random_state=42)","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:34:59.336412Z","iopub.execute_input":"2023-02-04T10:34:59.336900Z","iopub.status.idle":"2023-02-04T10:34:59.568344Z","shell.execute_reply.started":"2023-02-04T10:34:59.336866Z","shell.execute_reply":"2023-02-04T10:34:59.567290Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Text to Numerical Features using TF-IDF\nExpand to read how `TfidfVectorizer` work","metadata":{}},{"cell_type":"markdown","source":"The `TfidfVectorizer` class in scikit-learn is a tool used to convert text data into numerical features that can be used as input to a machine learning model.\n\nTF-IDF stands for term frequency-inverse document frequency. It's a measure of the importance of a word in a document with respect to an entire corpus of documents. The idea behind TF-IDF is to give more weight to words that are rare or specific to a particular document, and less weight to words that are common across all documents.\n\nHere's how it works:\n\n1. Term frequency (TF): The term frequency is the number of times a word appears in a document, normalized by the total number of words in the document. The resulting value is a measure of the importance of a word within a single document.\n\n2. Inverse document frequency (IDF): The inverse document frequency is a measure of the rarity of a word across all documents in the corpus. It is calculated as the logarithm of the total number of documents divided by the number of documents containing the word.\n\n3. TF-IDF: The final value for each word is obtained by multiplying its term frequency by its inverse document frequency. This results in a value that is high for words that are rare or specific to a particular document, and low for words that are common across all documents.\n\nThe `TfidfVectorizer` class implements this method by first tokenizing the text data into individual words (or \"tokens\"), and then computing the TF-IDF values for each token in each document. The result is a sparse matrix of features, where each row represents a document and each column represents a word.","metadata":{"_kg_hide-input":true}},{"cell_type":"markdown","source":"The `TfidfVectorizer` class has built-in tokenization capabilities, so you can simply pass it the raw text data and it will handle the tokenization for you.","metadata":{}},{"cell_type":"code","source":"# Convert the text data into numerical features using TF-IDF\nvectorizer = TfidfVectorizer()\nX_train_features = vectorizer.fit_transform(X_train)\nX_test_features = vectorizer.transform(X_test)","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:34:59.569645Z","iopub.execute_input":"2023-02-04T10:34:59.569993Z","iopub.status.idle":"2023-02-04T10:36:27.295539Z","shell.execute_reply.started":"2023-02-04T10:34:59.569963Z","shell.execute_reply":"2023-02-04T10:36:27.293701Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(X_train_features.shape)\nprint(X_test_features.shape)\n#print(vectorizer.vocabulary_)","metadata":{"execution":{"iopub.status.busy":"2023-02-04T11:35:20.619933Z","iopub.execute_input":"2023-02-04T11:35:20.620453Z","iopub.status.idle":"2023-02-04T11:35:20.628956Z","shell.execute_reply.started":"2023-02-04T11:35:20.620411Z","shell.execute_reply":"2023-02-04T11:35:20.627338Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Train a Logistic Regression Model","metadata":{}},{"cell_type":"code","source":"from sklearn.linear_model import LogisticRegression","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:36:27.297601Z","iopub.execute_input":"2023-02-04T10:36:27.298421Z","iopub.status.idle":"2023-02-04T10:36:27.304820Z","shell.execute_reply.started":"2023-02-04T10:36:27.298381Z","shell.execute_reply":"2023-02-04T10:36:27.303443Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Train a logistic regression model on the training data\n#model = LogisticRegression()\nmodel = LogisticRegression(class_weight='balanced')\nmodel.fit(X_train_features, y_train)","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:36:27.306256Z","iopub.execute_input":"2023-02-04T10:36:27.306613Z","iopub.status.idle":"2023-02-04T10:39:36.600353Z","shell.execute_reply.started":"2023-02-04T10:36:27.306580Z","shell.execute_reply":"2023-02-04T10:39:36.599452Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Make Predictions","metadata":{}},{"cell_type":"code","source":"# Make predictions on the test data\ny_pred = model.predict(X_test_features)","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:39:36.601518Z","iopub.execute_input":"2023-02-04T10:39:36.602407Z","iopub.status.idle":"2023-02-04T10:39:36.773457Z","shell.execute_reply.started":"2023-02-04T10:39:36.602374Z","shell.execute_reply":"2023-02-04T10:39:36.772345Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Evaluate","metadata":{}},{"cell_type":"code","source":"# Evaluate the model's accuracy\naccuracy = accuracy_score(y_test, y_pred)\nprint(\"Accuracy:\", accuracy)","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:39:36.774684Z","iopub.execute_input":"2023-02-04T10:39:36.775749Z","iopub.status.idle":"2023-02-04T10:39:36.793793Z","shell.execute_reply.started":"2023-02-04T10:39:36.775685Z","shell.execute_reply":"2023-02-04T10:39:36.792923Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This score is similar to submission score. <br/>\nBut going to submit it anyway :'>","metadata":{}},{"cell_type":"markdown","source":"## Predicts on unseen data","metadata":{}},{"cell_type":"code","source":"dfTest = pd.read_csv('/kaggle/input/goodreads-books-reviews-290312/goodreads_test.csv',\n                usecols=['review_id', 'review_text'])","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:39:36.795074Z","iopub.execute_input":"2023-02-04T10:39:36.795792Z","iopub.status.idle":"2023-02-04T10:39:42.522359Z","shell.execute_reply.started":"2023-02-04T10:39:36.795721Z","shell.execute_reply":"2023-02-04T10:39:42.521134Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dfTest.head()","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:39:42.523956Z","iopub.execute_input":"2023-02-04T10:39:42.524424Z","iopub.status.idle":"2023-02-04T10:39:42.536230Z","shell.execute_reply.started":"2023-02-04T10:39:42.524377Z","shell.execute_reply":"2023-02-04T10:39:42.534956Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dfTest = dfTest.assign(pre_text = preprocess_desc(dfTest['review_text']))","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:39:42.537958Z","iopub.execute_input":"2023-02-04T10:39:42.538374Z","iopub.status.idle":"2023-02-04T10:44:04.990217Z","shell.execute_reply.started":"2023-02-04T10:39:42.538323Z","shell.execute_reply":"2023-02-04T10:44:04.988913Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dfTest","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:44:04.992268Z","iopub.execute_input":"2023-02-04T10:44:04.992749Z","iopub.status.idle":"2023-02-04T10:44:05.016887Z","shell.execute_reply.started":"2023-02-04T10:44:04.992686Z","shell.execute_reply":"2023-02-04T10:44:05.015702Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"unseen_features = vectorizer.transform(dfTest['pre_text'])","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:44:05.018914Z","iopub.execute_input":"2023-02-04T10:44:05.019533Z","iopub.status.idle":"2023-02-04T10:44:51.764855Z","shell.execute_reply.started":"2023-02-04T10:44:05.019479Z","shell.execute_reply":"2023-02-04T10:44:51.763844Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(unseen_features.shape)","metadata":{"execution":{"iopub.status.busy":"2023-02-04T11:35:48.850488Z","iopub.execute_input":"2023-02-04T11:35:48.850983Z","iopub.status.idle":"2023-02-04T11:35:48.857833Z","shell.execute_reply.started":"2023-02-04T11:35:48.850944Z","shell.execute_reply":"2023-02-04T11:35:48.856148Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Use the model to make predictions on the new data\nunseen_predictions = model.predict(unseen_features)\n\nprint(\"Predictions:\", unseen_predictions)","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:44:51.766153Z","iopub.execute_input":"2023-02-04T10:44:51.766516Z","iopub.status.idle":"2023-02-04T10:44:52.187566Z","shell.execute_reply.started":"2023-02-04T10:44:51.766484Z","shell.execute_reply":"2023-02-04T10:44:52.186290Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create submission dataframe\nsubmission = pd.DataFrame({'review_id': dfTest['review_id'],\n                           'rating': unseen_predictions})","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:44:52.188928Z","iopub.execute_input":"2023-02-04T10:44:52.189283Z","iopub.status.idle":"2023-02-04T10:44:52.203469Z","shell.execute_reply.started":"2023-02-04T10:44:52.189250Z","shell.execute_reply":"2023-02-04T10:44:52.202297Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:44:52.204559Z","iopub.execute_input":"2023-02-04T10:44:52.205217Z","iopub.status.idle":"2023-02-04T10:44:52.218901Z","shell.execute_reply.started":"2023-02-04T10:44:52.205182Z","shell.execute_reply":"2023-02-04T10:44:52.217679Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Export submission file for submit to competition\nsubmission.to_csv(\"submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2023-02-04T10:44:52.220328Z","iopub.execute_input":"2023-02-04T10:44:52.220662Z","iopub.status.idle":"2023-02-04T10:44:52.954791Z","shell.execute_reply.started":"2023-02-04T10:44:52.220624Z","shell.execute_reply":"2023-02-04T10:44:52.953301Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Lemme do it for you :'>\n- Why training with `LogisticRegression` with ignoring about imbalanced data having better score :<\n    - 0.52771 | `model = LogisticRegression()`\n    - 0.47177 | `model = LogisticRegression(class_weight='balanced')` \n\n![Dog Zoiudo GIF](https://media.tenor.com/cbU3h2FqAGQAAAAd/dog-zoiudo.gif)","metadata":{}},{"cell_type":"code","source":"# Still need more learning :'<","metadata":{},"execution_count":null,"outputs":[]}]}