import math

import numpy as np
import pandas as pd
from sklearn import metrics
import tensorflow as tf
from tensorflow.python.data import Dataset

import plotly.plotly as py
import plotly.graph_objs as go

# Set Tensorflow logging verbosity
tf.logging.set_verbosity(tf.logging.ERROR)

# set pandas display options; used by the describe function
pd.options.display.max_rows = 10
pd.options.display.float_format = '{:.1f}'.format

# load the csv into a Pandas dataframe (table essentially; one with multiple series)
california_housing_dataframe = pd.read_csv("https://storage.googleapis.com/mledu-datasets/california_housing_train.csv", sep=",")

# randomize the rows of the dataframe (i.e table)
california_housing_dataframe = california_housing_dataframe.reindex(
    np.random.permutation(california_housing_dataframe.index))

# scale down the median value by 1000
california_housing_dataframe["median_house_value"] /= 1000.0 

# Define the input feature: total_rooms.
california_housing_dataframe["rooms_per_person"] = (california_housing_dataframe["total_rooms"]/california_housing_dataframe["population"]).apply(lambda x: min(x,5))
my_feature = california_housing_dataframe[["rooms_per_person"]]
print california_housing_dataframe["rooms_per_person"].describe()

# Configure a numeric feature column for total_rooms.
feature_columns = [tf.feature_column.numeric_column("rooms_per_person")]

# Define the label.
targets = california_housing_dataframe["median_house_value"]

# Use gradient descent as the optimizer for training the model.
# we are using gradient descent at the method to train the model.
my_optimizer=tf.train.GradientDescentOptimizer(learning_rate=0.5)
my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)

# Configure the linear regression model with our feature columns and optimizer.
# Set a learning rate of 0.0000001 for Gradient Descent.
linear_regressor = tf.estimator.LinearRegressor(
    feature_columns=feature_columns,
    optimizer=my_optimizer
)

def my_input_fn(features, targets, batch_size=100, shuffle=True, num_epochs=None):
    """Trains a linear regression model of one feature.
  
    Args:
      features: pandas DataFrame of features
      targets: pandas DataFrame of targets
      batch_size: Size of batches to be passed to the model
      shuffle: True or False. Whether to shuffle the data.
      num_epochs: Number of epochs for which data should be repeated. None = repeat indefinitely
    Returns:
      Tuple of (features, labels) for next data batch
    """
  
    # Convert pandas data into a dict of np arrays.
    features = {key:np.array(value) for key,value in dict(features).items()}                                           
 
    # Construct a dataset, and configure batching/repeating
    ds = Dataset.from_tensor_slices((features,targets)) # warning: 2GB limit
    ds = ds.batch(batch_size).repeat(num_epochs)
    
    # Shuffle the data, if specified
    if shuffle:
      ds = ds.shuffle(buffer_size=10000)
    
    # Return the next batch of data
    features, labels = ds.make_one_shot_iterator().get_next()
    return features, labels

_ = linear_regressor.train(
    input_fn = lambda:my_input_fn(my_feature, targets), 
    steps=500
)

# Create an input function for predictions.
# Note: Since we're making just one prediction for each example, we don't 
# need to repeat or shuffle the data here.
prediction_input_fn =lambda: my_input_fn(my_feature, targets, num_epochs=1, shuffle=False)

# Call predict() on the linear_regressor to make predictions.
predictions = linear_regressor.predict(input_fn=prediction_input_fn)

# Format predictions as a NumPy array, so we can calculate error metrics.
predictions = np.array([item['predictions'][0] for item in predictions])

# Print Mean Squared Error and Root Mean Squared Error.
mean_squared_error = metrics.mean_squared_error(predictions, targets)
root_mean_squared_error = math.sqrt(mean_squared_error)
print "Mean Squared Error (on training data): %0.3f" % mean_squared_error
print "Root Mean Squared Error (on training data): %0.3f" % root_mean_squared_error

def drawModel(sample):
    x_0 = sample.rooms_per_person.min()
    x_1 = sample.rooms_per_person.max()

    # Retrieve the final weight and bias generated during training.
    weight = linear_regressor.get_variable_value('linear/linear_model/rooms_per_person/weights')[0]
    bias = linear_regressor.get_variable_value('linear/linear_model/bias_weights')

    # Get the predicted median_house_values for the min and max total_rooms values.
    y_0 = weight * x_0 + bias 
    y_1 = weight * x_1 + bias 

    trace1 = go.Scatter(
        x = sample.rooms_per_person,
        y = sample.median_house_value,
        mode='markers',
        name='data'
    )
    
    trace2 = go.Scatter(
        x = [x_0,x_1],
        y = [y_0,y_1],
        mode='lines',
        name='model'
    )

    data = [trace1, trace2]
    py.plot(data, filename='scatter-for-dashboard')

    return

#drawModel()