def linear_regression():
    print("""
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression

# simple dataset
data = {
    "Hours":[1,2,3,4,5,6,7,8],
    "Score":[10,20,30,40,50,60,70,80]
}

df = pd.DataFrame(data)

# input and output
X = df[["Hours"]]
y = df["Score"]

# split data
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2)

# create model
model = LinearRegression()

# train model
model.fit(X_train,y_train)

# prediction
pred = model.predict([[9]])

print("Predicted score for 9 hours:",pred)
""")

def logistic_regression():
    print("""
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

# dataset
data = {
    "Hours":[1,2,3,4,5,6,7,8],
    "Pass":[0,0,0,0,1,1,1,1]
}

df = pd.DataFrame(data)

X = df[["Hours"]]
y = df["Pass"]

X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2)

model = LogisticRegression()

model.fit(X_train,y_train)

pred = model.predict([[6]])

print("Will student pass (1=yes 0=no):",pred)
""")


def decision_tree():
    print("""
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

data = {
    "Hours":[1,2,3,4,5,6,7,8],
    "Pass":[0,0,0,0,1,1,1,1]
}

df = pd.DataFrame(data)

X = df[["Hours"]]
y = df["Pass"]

X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2)

model = DecisionTreeClassifier()

model.fit(X_train,y_train)

pred = model.predict([[6]])

print("Prediction:",pred)
""")

def perceptron():
    print("""
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Perceptron

data = {
    "Hours":[1,2,3,4,5,6,7,8],
    "Pass":[0,0,0,0,1,1,1,1]
}

df = pd.DataFrame(data)

X = df[["Hours"]]
y = df["Pass"]

X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2)

model = Perceptron()

model.fit(X_train,y_train)

pred = model.predict([[7]])

print("Prediction:",pred)
""")


def mlp():
    print("""
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier

data = {
    "Hours":[1,2,3,4,5,6,7,8],
    "Pass":[0,0,0,0,1,1,1,1]
}

df = pd.DataFrame(data)

X = df[["Hours"]]
y = df["Pass"]

X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2)

model = MLPClassifier()

model.fit(X_train,y_train)

pred = model.predict([[7]])

print("Prediction:",pred)
""")

def simple_nlp():
    print("""
# Import libraries
import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB


# ------------------------------
# STEP 1: Create Text Dataset
# ------------------------------

data = {
    "text":[
        "win free money",
        "claim your prize",
        "hello how are you",
        "lets meet tomorrow",
        "win cash now"
    ],
    "label":[
        "spam",
        "spam",
        "normal",
        "normal",
        "spam"
    ]
}

df = pd.DataFrame(data)

print("Dataset")
print(df)


# ------------------------------
# STEP 2: Convert Text to Numbers
# ------------------------------

# computers cannot understand words
# so we convert text into numbers

vectorizer = CountVectorizer()

X = vectorizer.fit_transform(df["text"])

y = df["label"]

print("Words:",vectorizer.get_feature_names_out())


# ------------------------------
# STEP 3: Train Model
# ------------------------------

model = MultinomialNB()

model.fit(X,y)

print("Model Trained")


# ------------------------------
# STEP 4: Predict New Sentence
# ------------------------------

sentence = ["win money now"]

sentence_vector = vectorizer.transform(sentence)

prediction = model.predict(sentence_vector)

print("Sentence:",sentence)
print("Prediction:",prediction)
""")


def rnn_model():
    print("""
# Import libraries
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import SimpleRNN, Dense
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences


# ------------------------------
# STEP 1: Create Dataset
# ------------------------------

sentences = [
    "i love ai",
    "ai is amazing",
    "i hate waiting",
    "this is bad"
]

labels = [1,1,0,0]   # 1 = positive, 0 = negative


# ------------------------------
# STEP 2: Convert Words to Numbers
# ------------------------------

tokenizer = Tokenizer()
tokenizer.fit_on_texts(sentences)

sequences = tokenizer.texts_to_sequences(sentences)

padded = pad_sequences(sequences)

print("Sequences:", padded)


# ------------------------------
# STEP 3: Build RNN Model
# ------------------------------

model = Sequential()

model.add(SimpleRNN(10, input_shape=(padded.shape[1],1)))

model.add(Dense(1, activation="sigmoid"))

model.compile(
    optimizer="adam",
    loss="binary_crossentropy",
    metrics=["accuracy"]
)


# ------------------------------
# STEP 4: Train Model
# ------------------------------

X = padded.reshape((padded.shape[0], padded.shape[1],1))

model.fit(X, labels, epochs=50, verbose=0)

print("Model Trained")


# ------------------------------
# STEP 5: Predict
# ------------------------------

test = ["i love waiting"]

test_seq = tokenizer.texts_to_sequences(test)
test_pad = pad_sequences(test_seq, maxlen=padded.shape[1])

test_pad = test_pad.reshape((1,test_pad.shape[1],1))

prediction = model.predict(test_pad)

print("Prediction:", prediction)
""")

def preprocessing_models():
    print("""
# Import libraries
import pandas as pd
import numpy as np

from sklearn.model_selection import train_test_split

# Encoding
from sklearn.preprocessing import LabelEncoder, OrdinalEncoder

# Scaling
from sklearn.preprocessing import StandardScaler, MinMaxScaler

# Models
from sklearn.linear_model import LinearRegression, LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.neural_network import MLPClassifier

# Evaluation
from sklearn import metrics


# -----------------------------
# STEP 1 : Dataset
# -----------------------------

data = {
    "Age":[22,25,30,35,40],
    "City":["Delhi","Mumbai","Delhi","Chennai","Mumbai"],
    "Level":["Low","Medium","High","Medium","High"],
    "Salary":[20000,30000,40000,50000,60000],
    "Purchased":["No","No","Yes","Yes","Yes"]
}

df = pd.DataFrame(data)

print("Original Data")
print(df)


# -----------------------------
# STEP 2 : Encoding
# -----------------------------

# Label Encoding (used for target variable)

label = LabelEncoder()
df["Purchased"] = label.fit_transform(df["Purchased"])


# Ordinal Encoding (used when order matters)

ordinal = OrdinalEncoder()
df[["Level"]] = ordinal.fit_transform(df[["Level"]])


# One Hot Encoding (used for city or categories)

df = pd.get_dummies(df, columns=["City"])

print("\\nAfter Encoding")
print(df)


# -----------------------------
# STEP 3 : Feature / Target
# -----------------------------

X = df.drop("Purchased", axis=1)
y = df["Purchased"]

X_train,X_test,y_train,y_test = train_test_split(
    X,y,test_size=0.2
)


# -----------------------------
# STEP 4 : Scaling
# -----------------------------

# StandardScaler
# Used for Linear Regression, Logistic Regression, Neural Networks

scaler = StandardScaler()

X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)


# MinMaxScaler
# Often used for Neural Networks

minmax = MinMaxScaler()

X_train_minmax = minmax.fit_transform(X_train)
X_test_minmax = minmax.transform(X_test)


# -----------------------------
# STEP 5 : Linear Regression
# -----------------------------
# Used for predicting continuous numbers

lin_model = LinearRegression()

lin_model.fit(X_train_scaled, y_train)

pred_lin = lin_model.predict(X_test_scaled)

print("\\nLinear Regression")

print("R2:",metrics.r2_score(y_test,pred_lin))
print("MSE:",metrics.mean_squared_error(y_test,pred_lin))
print("RMSE:",np.sqrt(metrics.mean_squared_error(y_test,pred_lin)))


# -----------------------------
# STEP 6 : Logistic Regression
# -----------------------------
# Used for classification (Yes / No)

log_model = LogisticRegression()

log_model.fit(X_train_scaled, y_train)

pred_log = log_model.predict(X_test_scaled)

print("\\nLogistic Regression")

print("Accuracy:",metrics.accuracy_score(y_test,pred_log))


# -----------------------------
# STEP 7 : Decision Tree
# -----------------------------
# Works without scaling

tree_model = DecisionTreeClassifier()

tree_model.fit(X_train, y_train)

pred_tree = tree_model.predict(X_test)

print("\\nDecision Tree")

print("Accuracy:",metrics.accuracy_score(y_test,pred_tree))


# -----------------------------
# STEP 8 : Neural Network (MLP)
# -----------------------------
# Works best with scaled data

mlp_model = MLPClassifier(max_iter=500)

mlp_model.fit(X_train_minmax, y_train)

pred_mlp = mlp_model.predict(X_test_minmax)

print("\\nMLP")

print("Accuracy:",metrics.accuracy_score(y_test,pred_mlp))

""")


def eda_functions():
    print("""
# Import libraries
import pandas as pd
import numpy as np


# -------------------------
# STEP 1 : Create Dataset
# -------------------------

data = {
    "Age":[22,25,30,35,40,100],
    "Salary":[20000,30000,40000,50000,60000,1000000],
    "City":["Delhi","Mumbai","Delhi","Chennai","Mumbai","Delhi"]
}

df = pd.DataFrame(data)

print("Dataset")
print(df)


# -------------------------
# BASIC EDA
# -------------------------

print(df.head())       # first rows
print(df.tail())       # last rows
print(df.shape)        # rows and columns
print(df.columns)      # column names
print(df.info())       # data types
print(df.describe())   # statistics


# -------------------------
# NUMPY BASIC OPERATIONS
# -------------------------

arr = np.array(df["Salary"])

print(np.mean(arr))
print(np.median(arr))
print(np.std(arr))
print(np.max(arr))
print(np.min(arr))


# -------------------------
# MISSING VALUES
# -------------------------

print(df.isnull().sum())   # count missing values

df["Age"] = df["Age"].fillna(df["Age"].mean())


# -------------------------
# FILTERING DATA
# -------------------------

print(df[df["Age"] > 30])


# -------------------------
# SORTING
# -------------------------

print(df.sort_values("Salary"))


# -------------------------
# GROUPING
# -------------------------

print(df.groupby("City").mean())


# -------------------------
# ADD NEW COLUMN
# -------------------------

df["Bonus"] = df["Salary"] * 0.1

print(df)


# -------------------------
# REMOVE DUPLICATES
# -------------------------

df.drop_duplicates()


# -------------------------
# OUTLIER DETECTION (IQR)
# -------------------------

Q1 = df["Salary"].quantile(0.25)
Q3 = df["Salary"].quantile(0.75)

IQR = Q3 - Q1

lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR

df_clean = df[(df["Salary"] >= lower) & (df["Salary"] <= upper)]

print("After removing outliers")
print(df_clean)


# -------------------------
# CORRELATION
# -------------------------

print(df.corr())


# -------------------------
# NUMPY ARRAY OPERATIONS
# -------------------------

a = np.array([1,2,3,4])

print(np.sum(a))
print(np.sqrt(a))
print(np.log(a))
print(np.square(a))
""")


def linear_regression_from_scratch():
    print("""
import numpy as np

# -------------------------
# STEP 1 : Dataset
# -------------------------

# Hours studied
X = np.array([1,2,3,4,5])

# Scores
y = np.array([10,20,30,40,50])


# -------------------------
# STEP 2 : Calculate Mean
# -------------------------

mean_x = np.mean(X)
mean_y = np.mean(y)


# -------------------------
# STEP 3 : Calculate slope (m)
# formula:
# m = Σ((x - x̄)(y - ȳ)) / Σ((x - x̄)^2)

num = np.sum((X - mean_x) * (y - mean_y))
den = np.sum((X - mean_x) ** 2)

m = num / den


# -------------------------
# STEP 4 : Calculate intercept (b)
# formula:
# b = ȳ - m*x̄

b = mean_y - m * mean_x


print("Slope (m):", m)
print("Intercept (b):", b)


# -------------------------
# STEP 5 : Prediction
# -------------------------

y_pred = m * X + b

print("Predictions:", y_pred)


# -------------------------
# STEP 6 : Evaluation
# -------------------------

# MSE
mse = np.mean((y - y_pred) ** 2)

# RMSE
rmse = np.sqrt(mse)

# R2 Score
ss_total = np.sum((y - mean_y) ** 2)
ss_res = np.sum((y - y_pred) ** 2)

r2 = 1 - (ss_res / ss_total)


print("MSE:", mse)
print("RMSE:", rmse)
print("R2:", r2)
""")


def mlp_from_scratch():
    print("""
import numpy as np

# -------------------------
# STEP 1 : Dataset
# -------------------------

# XOR problem
X = np.array([
    [0,0],
    [0,1],
    [1,0],
    [1,1]
])

y = np.array([[0],[1],[1],[0]])


# -------------------------
# STEP 2 : Initialize weights
# -------------------------

np.random.seed(0)

input_size = 2
hidden_size = 4
output_size = 1

W1 = np.random.randn(input_size, hidden_size)
b1 = np.zeros((1, hidden_size))

W2 = np.random.randn(hidden_size, output_size)
b2 = np.zeros((1, output_size))


# -------------------------
# STEP 3 : Activation Function
# -------------------------

def sigmoid(x):
    return 1/(1+np.exp(-x))

def sigmoid_derivative(x):
    return x*(1-x)


# -------------------------
# STEP 4 : Training Loop
# -------------------------

learning_rate = 0.1
epochs = 10000

for i in range(epochs):

    # -------- Forward Propagation --------

    z1 = np.dot(X, W1) + b1
    a1 = sigmoid(z1)

    z2 = np.dot(a1, W2) + b2
    output = sigmoid(z2)


    # -------- Calculate Error --------

    error = y - output


    # -------- Backpropagation --------

    d_output = error * sigmoid_derivative(output)

    d_hidden = np.dot(d_output, W2.T) * sigmoid_derivative(a1)


    # -------- Update Weights --------

    W2 += np.dot(a1.T, d_output) * learning_rate
    b2 += np.sum(d_output, axis=0, keepdims=True) * learning_rate

    W1 += np.dot(X.T, d_hidden) * learning_rate
    b1 += np.sum(d_hidden, axis=0, keepdims=True) * learning_rate


# -------------------------
# STEP 5 : Final Prediction
# -------------------------

print("Predictions:")
print(output)
""")


def help_ml():
    print("Available models:")
    print("1. linear_regression()")
    print("2. logistic_regression()")
    print("3. decision_tree()")
    print("4. perceptron()")
    print("5. mlp()")
    print("6. simple_nlp()")
    print("7. rnn_model()")
    print("8. preprocessing_models()")
    print("9. eda_functions()")
    print("10. linear_regression_from_scratch()")
    print("11. mlp_from_scratch()")
    





if __name__ == "__main__":
    decision_tree()