# Notebook from lliimsft/MachineLearningNotebooks
Path: how-to-use-azureml/deployment/onnx/onnx-inference-mnist-deploy.ipynb

Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License._____no_output_____# Handwritten Digit Classification (MNIST) using ONNX Runtime on Azure ML

This example shows how to deploy an image classification neural network using the Modified National Institute of Standards and Technology ([MNIST](http://yann.lecun.com/exdb/mnist/)) dataset and Open Neural Network eXchange format ([ONNX](http://aka.ms/onnxdocarticle)) on the Azure Machine Learning platform. MNIST is a popular dataset consisting of 70,000 grayscale images. Each image is a handwritten digit of 28x28 pixels, representing number from 0 to 9. This tutorial will show you how to deploy a MNIST model from the [ONNX model zoo](https://github.com/onnx/models), use it to make predictions using ONNX Runtime Inference, and deploy it as a web service in Azure.

Throughout this tutorial, we will be referring to ONNX, a neural network exchange format used to represent deep learning models. With ONNX, AI developers can more easily move models between state-of-the-art tools (CNTK, PyTorch, Caffe, MXNet, TensorFlow) and choose the combination that is best for them. ONNX is developed and supported by a community of partners including Microsoft AI, Facebook, and Amazon. For more information, explore the [ONNX website](http://onnx.ai) and [open source files](https://github.com/onnx).

[ONNX Runtime](https://aka.ms/onnxruntime-python) is the runtime engine that enables evaluation of trained machine learning (Traditional ML and Deep Learning) models with high performance and low resource utilization.

#### Tutorial Objectives:

- Describe the MNIST dataset and pretrained Convolutional Neural Net ONNX model, stored in the ONNX model zoo.
- Deploy and run the pretrained MNIST ONNX model on an Azure Machine Learning instance
- Predict labels for test set data points in the cloud using ONNX Runtime and Azure ML_____no_output_____## Prerequisites

### 1. Install Azure ML SDK and create a new workspace
Please follow [Azure ML configuration notebook](https://github.com/Azure/MachineLearningNotebooks/blob/master/00.configuration.ipynb) to set up your environment.

### 2. Install additional packages needed for this tutorial notebook
You need to install the popular plotting library `matplotlib`, the image manipulation library `opencv`, and the `onnx` library in the conda environment where Azure Maching Learning SDK is installed.

```sh
(myenv) $ pip install matplotlib onnx opencv-python
```

**Debugging tip**: Make sure that you run the "jupyter notebook" command to launch this notebook after activating your virtual environment. Choose the respective Python kernel for your new virtual environment using the `Kernel > Change Kernel` menu above. If you have completed the steps correctly, the upper right corner of your screen should state `Python [conda env:myenv]` instead of `Python [default]`.

### 3. Download sample data and pre-trained ONNX model from ONNX Model Zoo.

In the following lines of code, we download [the trained ONNX MNIST model and corresponding test data](https://github.com/onnx/models/tree/master/mnist) and place them in the same folder as this tutorial notebook. For more information about the MNIST dataset, please visit [Yan LeCun's website](http://yann.lecun.com/exdb/mnist/)._____no_output_____

# urllib is a built-in Python library to download files from URLs

# Objective: retrieve the latest version of the ONNX MNIST model files from the
# ONNX Model Zoo and save it in the same folder as this tutorial

import urllib.request

onnx_model_url = "https://www.cntk.ai/OnnxModels/mnist/opset_7/mnist.tar.gz"

urllib.request.urlretrieve(onnx_model_url, filename="mnist.tar.gz")_____no_output_____# the ! magic command tells our jupyter notebook kernel to run the following line of
# code from the command line instead of the notebook kernel

# We use tar and xvcf to unzip the files we just retrieved from the ONNX model zoo

!tar xvzf mnist.tar.gz_____no_output_____

## Deploy a VM with your ONNX model in the Cloud

### Load Azure ML workspace

We begin by instantiating a workspace object from the existing workspace created earlier in the configuration notebook._____no_output_____

# Check core SDK version number
import azureml.core

print("SDK version:", azureml.core.VERSION)_____no_output_____from azureml.core import Workspace

ws = Workspace.from_config()
print(ws.name, ws.resource_group, ws.location, sep = '\n')_____no_output_____

### Registering your model with Azure ML_____no_output_____

model_dir = "mnist" # replace this with the location of your model files

# leave as is if it's in the same folder as this notebook_____no_output_____from azureml.core.model import Model

model = Model.register(workspace = ws,
                       model_path = model_dir + "/" + "model.onnx",
                       model_name = "mnist_1",
                       tags = {"onnx": "demo"},
                       description = "MNIST image classification CNN from ONNX Model Zoo",)_____no_output_____

### Optional: Displaying your registered models

This step is not required, so feel free to skip it._____no_output_____

models = ws.models
for name, m in models.items():
    print("Name:", name,"\tVersion:", m.version, "\tDescription:", m.description, m.tags)_____no_output_____

### ONNX MNIST Model Methodology

The image classification model we are using is pre-trained using Microsoft's deep learning cognitive toolkit, [CNTK](https://github.com/Microsoft/CNTK), from the [ONNX model zoo](http://github.com/onnx/models). The model zoo has many other models that can be deployed on cloud providers like AzureML without any additional training. To ensure that our cloud deployed model works, we use testing data from the famous MNIST data set, provided as part of the [trained MNIST model](https://github.com/onnx/models/tree/master/mnist) in the ONNX model zoo.

***Input: Handwritten Images from MNIST Dataset***

***Task: Classify each MNIST image into an appropriate digit***

***Output: Digit prediction for input image***

Run the cell below to look at some of the sample images from the MNIST dataset that we used to train this ONNX model. Remember, once the application is deployed in Azure ML, you can use your own images as input for the model to classify!_____no_output_____

# for images and plots in this notebook
import matplotlib.pyplot as plt
from IPython.display import Image

# display images inline
%matplotlib inline_____no_output_____Image(url="http://3.bp.blogspot.com/_UpN7DfJA0j4/TJtUBWPk0SI/AAAAAAAAABY/oWPMtmqJn3k/s1600/mnist_originals.png", width=200, height=200)_____no_output_____

### Specify our Score and Environment Files_____no_output_____We are now going to deploy our ONNX Model on AML with inference in ONNX Runtime. We begin by writing a score.py file, which will help us run the model in our Azure ML virtual machine (VM), and then specify our environment by writing a yml file. You will also notice that we import the onnxruntime library to do runtime inference on our ONNX models (passing in input and evaluating out model's predicted output). More information on the API and commands can be found in the [ONNX Runtime documentation](https://aka.ms/onnxruntime).

### Write Score File

A score file is what tells our Azure cloud service what to do. After initializing our model using azureml.core.model, we start an ONNX Runtime inference session to evaluate the data passed in on our function calls._____no_output_____

%%writefile score.py
import json
import numpy as np
import onnxruntime
import sys
import os
from azureml.core.model import Model
import time

def init():
    global session, input_name, output_name
    model = Model.get_model_path(model_name = 'mnist_1')
    session = onnxruntime.InferenceSession(model, None)
    input_name = session.get_inputs()[0].name
    output_name = session.get_outputs()[0].name

def preprocess(input_data_json):
    # convert the JSON data into the tensor input
    return np.array(json.loads(input_data_json)['data']).astype('float32')

def postprocess(result):
    # We use argmax to pick the highest confidence label
    return int(np.argmax(np.array(result).squeeze(), axis=0))

def run(input_data):

    try:
        # load in our data, convert to readable format
        data = preprocess(input_data)

        # start timer
        start = time.time()

        r = session.run([output_name], {input_name: data})

        #end timer
        end = time.time()

        result = postprocess(r)
        result_dict = {"result": result,
                      "time_in_sec": end - start}
    except Exception as e:
        result_dict = {"error": str(e)}

    return result_dict

def choose_class(result_prob):
    """We use argmax to determine the right label to choose from our output"""
    return int(np.argmax(result_prob, axis=0))_____no_output_____

### Write Environment File

This step creates a YAML environment file that specifies which dependencies we would like to see in our Linux Virtual Machine._____no_output_____

from azureml.core.conda_dependencies import CondaDependencies

myenv = CondaDependencies.create(pip_packages=["numpy", "onnxruntime", "azureml-core"])

with open("myenv.yml","w") as f:
    f.write(myenv.serialize_to_string())_____no_output_____

### Create the Container Image
This step will likely take a few minutes._____no_output_____

from azureml.core.image import ContainerImage

image_config = ContainerImage.image_configuration(execution_script = "score.py",
                                                  runtime = "python",
                                                  conda_file = "myenv.yml",
                                                  description = "MNIST ONNX Runtime container",
                                                  tags = {"demo": "onnx"})

image = ContainerImage.create(name = "onnximage",
                              # this is the model object
                              models = [model],
                              image_config = image_config,
                              workspace = ws)

image.wait_for_creation(show_output = True)_____no_output_____

In case you need to debug your code, the next line of code accesses the log file._____no_output_____

print(image.image_build_log_uri)_____no_output_____

We're all done specifying what we want our virtual machine to do. Let's configure and deploy our container image.

### Deploy the container image_____no_output_____

from azureml.core.webservice import AciWebservice

aciconfig = AciWebservice.deploy_configuration(cpu_cores = 1,
                                               memory_gb = 1,
                                               tags = {'demo': 'onnx'},
                                               description = 'ONNX for mnist model')_____no_output_____

The following cell will likely take a few minutes to run as well._____no_output_____

from azureml.core.webservice import Webservice

aci_service_name = 'onnx-demo-mnist'
print("Service", aci_service_name)

aci_service = Webservice.deploy_from_image(deployment_config = aciconfig,
                                           image = image,
                                           name = aci_service_name,
                                           workspace = ws)

aci_service.wait_for_deployment(True)
print(aci_service.state)_____no_output_____if aci_service.state != 'Healthy':
    # run this command for debugging.
    print(aci_service.get_logs())

    # If your deployment fails, make sure to delete your aci_service or rename your service before trying again!
    # aci_service.delete()_____no_output_____

### Success!

If you've made it this far, you've deployed a working VM with a handwritten digit classifier running in the cloud using Azure ML. Congratulations!

You can get the URL for the webservice with the code below. Let's now see how well our model deals with our test images._____no_output_____

print(aci_service.scoring_uri)_____no_output_____

## Testing and Evaluation

### Load Test Data

These are already in your directory from your ONNX model download (from the model zoo).

Notice that our Model Zoo files have a .pb extension. This is because they are [protobuf files (Protocol Buffers)](https://developers.google.com/protocol-buffers/docs/pythontutorial), so we need to read in our data through our ONNX TensorProto reader into a format we can work with, like numerical arrays._____no_output_____

# to manipulate our arrays
import numpy as np

# read in test data protobuf files included with the model
import onnx
from onnx import numpy_helper

# to use parsers to read in our model/data
import json
import os

test_inputs = []
test_outputs = []

# read in 3 testing images from .pb files
test_data_size = 3

for i in np.arange(test_data_size):
    input_test_data = os.path.join(model_dir, 'test_data_set_{0}'.format(i), 'input_0.pb')
    output_test_data = os.path.join(model_dir, 'test_data_set_{0}'.format(i), 'output_0.pb')

    # convert protobuf tensors to np arrays using the TensorProto reader from ONNX
    tensor = onnx.TensorProto()
    with open(input_test_data, 'rb') as f:
        tensor.ParseFromString(f.read())

    input_data = numpy_helper.to_array(tensor)
    test_inputs.append(input_data)

    with open(output_test_data, 'rb') as f:
        tensor.ParseFromString(f.read())

    output_data = numpy_helper.to_array(tensor)
    test_outputs.append(output_data)

if len(test_inputs) == test_data_size:
    print('Test data loaded successfully.')_____no_output_____

### Show some sample images
We use `matplotlib` to plot 3 test images from the dataset._____no_output_____

plt.figure(figsize = (16, 6))
for test_image in np.arange(3):
    plt.subplot(1, 15, test_image+1)
    plt.axhline('')
    plt.axvline('')
    plt.imshow(test_inputs[test_image].reshape(28, 28), cmap = plt.cm.Greys)
plt.show()_____no_output_____

### Run evaluation / prediction_____no_output_____

plt.figure(figsize = (16, 6), frameon=False)
plt.subplot(1, 8, 1)

plt.text(x = 0, y = -30, s = "True Label: ", fontsize = 13, color = 'black')
plt.text(x = 0, y = -20, s = "Result: ", fontsize = 13, color = 'black')
plt.text(x = 0, y = -10, s = "Inference Time: ", fontsize = 13, color = 'black')
plt.text(x = 3, y = 14, s = "Model Input", fontsize = 12, color = 'black')
plt.text(x = 6, y = 18, s = "(28 x 28)", fontsize = 12, color = 'black')
plt.imshow(np.ones((28,28)), cmap=plt.cm.Greys)

for i in np.arange(test_data_size):

    input_data = json.dumps({'data': test_inputs[i].tolist()})

    # predict using the deployed model
    r = aci_service.run(input_data)

    if "error" in r:
        print(r['error'])
        break

    result = r['result']
    time_ms = np.round(r['time_in_sec'] * 1000, 2)

    ground_truth = int(np.argmax(test_outputs[i]))

    # compare actual value vs. the predicted values:
    plt.subplot(1, 8, i+2)
    plt.axhline('')
    plt.axvline('')

    # use different color for misclassified sample
    font_color = 'red' if ground_truth != result else 'black'
    clr_map = plt.cm.gray if ground_truth != result else plt.cm.Greys

    # ground truth labels are in blue
    plt.text(x = 10, y = -30, s = ground_truth, fontsize = 18, color = 'blue')

    # predictions are in black if correct, red if incorrect
    plt.text(x = 10, y = -20, s = result, fontsize = 18, color = font_color)
    plt.text(x = 5, y = -10, s = str(time_ms) + ' ms', fontsize = 14, color = font_color)

    plt.imshow(test_inputs[i].reshape(28, 28), cmap = clr_map)

plt.show()_____no_output_____

### Try classifying your own images!

Create your own handwritten image and pass it into the model._____no_output_____

# Preprocessing functions take your image and format it so it can be passed
# as input into our ONNX model

import cv2

def rgb2gray(rgb):
    """Convert the input image into grayscale"""
    return np.dot(rgb[...,:3], [0.299, 0.587, 0.114])

def resize_img(img):
    """Resize image to MNIST model input dimensions"""
    img = cv2.resize(img, dsize=(28, 28), interpolation=cv2.INTER_AREA)
    img.resize((1, 1, 28, 28))
    return img

def preprocess(img):
    """Resize input images and convert them to grayscale."""
    if img.shape == (28, 28):
        img.resize((1, 1, 28, 28))
        return img

    grayscale = rgb2gray(img)
    processed_img = resize_img(grayscale)
    return processed_img_____no_output_____# Replace this string with your own path/test image
# Make sure your image is square and the dimensions are equal (i.e. 100 * 100 pixels or 28 * 28 pixels)

# Any PNG or JPG image file should work

your_test_image = "<path to file>"

# e.g. your_test_image = "C:/Users/vinitra.swamy/Pictures/handwritten_digit.png"

import matplotlib.image as mpimg

if your_test_image != "<path to file>":
    img = mpimg.imread(your_test_image)
    plt.subplot(1,3,1)
    plt.imshow(img, cmap = plt.cm.Greys)
    print("Old Dimensions: ", img.shape)
    img = preprocess(img)
    print("New Dimensions: ", img.shape)
else:
    img = None_____no_output_____if img is None:
    print("Add the path for your image data.")
else:
    input_data = json.dumps({'data': img.tolist()})

    try:
        r = aci_service.run(input_data)
        result = r['result']
        time_ms = np.round(r['time_in_sec'] * 1000, 2)
    except Exception as e:
        print(str(e))

    plt.figure(figsize = (16, 6))
    plt.subplot(1, 15,1)
    plt.axhline('')
    plt.axvline('')
    plt.text(x = -100, y = -20, s = "Model prediction: ", fontsize = 14)
    plt.text(x = -100, y = -10, s = "Inference time: ", fontsize = 14)
    plt.text(x = 0, y = -20, s = str(result), fontsize = 14)
    plt.text(x = 0, y = -10, s = str(time_ms) + " ms", fontsize = 14)
    plt.text(x = -100, y = 14, s = "Input image: ", fontsize = 14)
    plt.imshow(img.reshape(28, 28), cmap = plt.cm.gray)    _____no_output_____

## Optional: How does our  ONNX MNIST model work?
#### A brief explanation of Convolutional Neural Networks

A [convolutional neural network](https://en.wikipedia.org/wiki/Convolutional_neural_network) (CNN, or ConvNet) is a type of [feed-forward](https://en.wikipedia.org/wiki/Feedforward_neural_network) artificial neural network made up of neurons that have learnable weights and biases. The CNNs take advantage of the spatial nature of the data. In nature, we perceive different objects by their shapes, size and colors. For example, objects in a natural scene are typically edges, corners/vertices (defined by two of more edges), color patches etc. These primitives are often identified using different detectors (e.g., edge detection, color detector) or combination of detectors interacting to facilitate image interpretation (object classification, region of interest detection, scene description etc.) in real world vision related tasks. These detectors are also known as filters. Convolution is a mathematical operator that takes an image and a filter as input and produces a filtered output (representing say edges, corners, or colors in the input image).

Historically, these filters are a set of weights that were often hand crafted or modeled with mathematical functions (e.g., [Gaussian](https://en.wikipedia.org/wiki/Gaussian_filter) / [Laplacian](http://homepages.inf.ed.ac.uk/rbf/HIPR2/log.htm) / [Canny](https://en.wikipedia.org/wiki/Canny_edge_detector) filter).  The filter outputs are mapped through non-linear activation functions mimicking human brain cells called [neurons](https://en.wikipedia.org/wiki/Neuron). Popular deep CNNs or ConvNets (such as [AlexNet](https://en.wikipedia.org/wiki/AlexNet), [VGG](https://arxiv.org/abs/1409.1556), [Inception](http://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Szegedy_Going_Deeper_With_2015_CVPR_paper.pdf), [ResNet](https://arxiv.org/pdf/1512.03385v1.pdf)) that are used for various [computer vision](https://en.wikipedia.org/wiki/Computer_vision) tasks have many of these architectural primitives (inspired from biology).

### Convolution Layer

A convolution layer is a set of filters. Each filter is defined by a weight (**W**) matrix, and  bias ($b$).

![](https://www.cntk.ai/jup/cntk103d_filterset_v2.png)

These filters are scanned across the image performing the dot product between the weights and corresponding input value ($x$). The bias value is added to the output of the dot product and the resulting sum is optionally mapped through an activation function. This process is illustrated in the following animation._____no_output_____

Image(url="https://www.cntk.ai/jup/cntk103d_conv2d_final.gif", width= 200)_____no_output_____

### Model Description

The MNIST model from the ONNX Model Zoo uses maxpooling to update the weights in its convolutions, summarized by the graphic below. You can see the entire workflow of our pre-trained model in the following image, with our input images and our output probabilities of each of our 10 labels. If you're interested in exploring the logic behind creating a Deep Learning model further, please look at the [training tutorial for our ONNX MNIST Convolutional Neural Network](https://github.com/Microsoft/CNTK/blob/master/Tutorials/CNTK_103D_MNIST_ConvolutionalNeuralNetwork.ipynb). _____no_output_____#### Max-Pooling for Convolutional Neural Nets

![](http://www.cntk.ai/jup/c103d_max_pooling.gif)_____no_output_____#### Pre-Trained Model Architecture

![](http://www.cntk.ai/jup/conv103d_mnist-conv-mp.png)_____no_output_____

# remember to delete your service after you are done using it!

# aci_service.delete()_____no_output_____

## Conclusion

Congratulations!

In this tutorial, you have:
- familiarized yourself with ONNX Runtime inference and the pretrained models in the ONNX model zoo
- understood a state-of-the-art convolutional neural net image classification model (MNIST in ONNX) and deployed it in Azure ML cloud
- ensured that your deep learning model is working perfectly (in the cloud) on test data, and checked it against some of your own!

Next steps:
- Check out another interesting application based on a Microsoft Research computer vision paper that lets you set up a [facial emotion recognition model](https://github.com/Azure/MachineLearningNotebooks/tree/master/onnx/onnx-inference-emotion-recognition.ipynb) in the cloud! This tutorial deploys a pre-trained ONNX Computer Vision model in an Azure ML virtual machine.
- Contribute to our [open source ONNX repository on github](http://github.com/onnx/onnx) and/or add to our [ONNX model zoo](http://github.com/onnx/models)_____no_output_____


# Notebook from leriomaggio/develer-data-science
Path: 2_alchemist/Before Leaving...ML sneak peek.ipynb

# Machine Learning (and Numpy Arrays)_____no_output_____Machine Learning is about building programs with **tunable parameters** (typically an
array of floating point values) that are adjusted automatically so as to improve
their behavior by **adapting to previously seen data.**

We'll take a look at a very simple machine learning tasks here: the **clustering** task_____no_output_____## *Data* for Machine Learning Algorithms_____no_output_____Data in machine learning algorithms, with very few exceptions, is assumed to be stored as a
**two-dimensional array**, of size `[n_samples, n_features]`.

The arrays can be
either ``numpy`` arrays, or in some cases ``scipy.sparse`` matrices.
The size of the array is expected to be `[n_samples, n_features]`

- **n_samples:**   The number of samples: each sample is an item to process (e.g. classify).
  A sample can be a document, a picture, a sound, a video, an astronomical object,
  a row in database or CSV file,
  or whatever you can describe with a fixed set of quantitative traits.

- **n_features:**  The number of features or distinct traits that can be used to describe each
  item in a quantitative manner.  Features are generally real-valued, but may be boolean or
  discrete-valued in some cases.

The number of features must be fixed in advance. However it can be very high dimensional
(e.g. millions of features) with most of them being zeros for a given sample.

This is a case where `scipy.sparse` matrices can be useful, in that they are much more memory-efficient than numpy arrays._____no_output_____## A Simple Example: the Iris Dataset_____no_output_____

from IPython.core.display import Image, display
display(Image(filename='../images/iris_setosa.jpg'))
print("Iris Setosa\n")

display(Image(filename='../images/iris_versicolor.jpg'))
print("Iris Versicolor\n")

display(Image(filename='../images/iris_virginica.jpg'))
print("Iris Virginica")_____no_output_____

- Features in the Iris dataset:

  1. sepal length in cm
  2. sepal width in cm
  3. petal length in cm
  4. petal width in cm

- Target classes to predict:

  1. Iris Setosa
  2. Iris Versicolour
  3. Iris Virginica_____no_output_____

from sklearn.datasets import load_iris
iris = load_iris()_____no_output_____

Try by yourself one of the following commands where *'d'* is the variable containing the dataset:

    print(iris.keys())           # Structure of the contained data
    print(iris.DESCR)            # A complete description of the dataset
    print(iris.data.shape)       # [n_samples, n_features]
    print(iris.target.shape)     # [n_samples,]
    print(iris.feature_names)
    datasets.get_data_home() # This is where the datasets are stored_____no_output_____

print(iris.keys())dict_keys(['DESCR', 'target_names', 'feature_names', 'target', 'data'])
print(iris.DESCR)Iris Plants Database

Notes
-----
Data Set Characteristics:
    :Number of Instances: 150 (50 in each of three classes)
    :Number of Attributes: 4 numeric, predictive attributes and the class
    :Attribute Information:
        - sepal length in cm
        - sepal width in cm
        - petal length in cm
        - petal width in cm
        - class:
                - Iris-Setosa
                - Iris-Versicolour
                - Iris-Virginica
    :Summary Statistics:

    ============== ==== ==== ======= ===== ====================
                    Min  Max   Mean    SD   Class Correlation
    ============== ==== ==== ======= ===== ====================
    sepal length:   4.3  7.9   5.84   0.83    0.7826
    sepal width:    2.0  4.4   3.05   0.43   -0.4194
    petal length:   1.0  6.9   3.76   1.76    0.9490  (high!)
    petal width:    0.1  2.5   1.20  0.76     0.9565  (high!)
    ============== ==== ==== ======= ===== ====================

    :Missing Attribute Values: None
    :Class Distribution: 33.3% for each of 3 classes.
    :Creator: R.A. Fisher
    :Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov)
    :Date: July, 1988

This is a copy of UCI ML iris datasets.
http://archive.ics.uci.edu/ml/datasets/Iris

The famous Iris database, first used by Sir R.A Fisher

This is perhaps the best known database to be found in the
pattern recognition literature.  Fisher's paper is a classic in the field and
is referenced frequently to this day.  (See Duda & Hart, for example.)  The
data set contains 3 classes of 50 instances each, where each class refers to a
type of iris plant.  One class is linearly separable from the other 2; the
latter are NOT linearly separable from each other.

References
----------
   - Fisher,R.A. "The use of multiple measurements in taxonomic problems"
     Annual Eugenics, 7, Part II, 179-188 (1936); also in "Contributions to
     Mathematical Statistics" (John Wiley, NY, 1950).
   - Duda,R.O., & Hart,P.E. (1973) Pattern Classification and Scene Analysis.
     (Q327.D83) John Wiley & Sons.  ISBN 0-471-22361-1.  See page 218.
   - Dasarathy, B.V. (1980) "Nosing Around the Neighborhood: A New System
     Structure and Classification Rule for Recognition in Partially Exposed
     Environments".  IEEE Transactions on Pattern Analysis and Machine
     Intelligence, Vol. PAMI-2, No. 1, 67-71.
   - Gates, G.W. (1972) "The Reduced Nearest Neighbor Rule".  IEEE Transactions
     on Information Theory, May 1972, 431-433.
   - See also: 1988 MLC Proceedings, 54-64.  Cheeseman et al"s AUTOCLASS II
     conceptual clustering system finds 3 classes in the data.
   - Many, many more ...

print(type(iris.data))<class 'numpy.ndarray'>
data = iris.data
print(data.size, data.shape)600 (150, 4)

## Spatial and Clustering Analysis

**Spatial** and **Clustering** analysis are key to identifying patterns, groups, and clusters in data.
In astrophysics, for example, these analysis techniques are used to seek and identify star clusters, galaxy clusters, and large-scale filaments (composed of galaxy clusters). _____no_output__________no_output__________no_output__________no_output_____## Machine Learning with SciPy_____no_output_____**SciPy** provides a spatial analysis class (`scipy.spatial`) and a cluster analysis class (`scipy.cluster`).

* The spatial class includes functions to analyze distances between data points (e.g., k-d trees).

* The cluster class provides two overarching subclasses:

    - vector quantization (`vq`);
    - hierarchical clustering (`hierarchy`).

**Vector quantization** groups large sets of data points (`vectors`) where each group is represented by centroids.

The **hierarchy** subclass contains functions to construct clusters and analyze their substructures.

(*We won't see examples of hierarchical clustering in this notebook*)_____no_output_____### Simple Example_____no_output_____

import numpy as np
np.random.seed(42)  # fixing random seed
from scipy.cluster import vq_____no_output_____# Creating randomly generated data
c1 = np.random.randn(100, 2) + 5
c2 = np.random.randn(30, 2) - 5
c3 = np.random.randn(50, 2)_____no_output_____# Pooling all the data into one 180 x 2 array
data = np.vstack([c1, c2, c3])_____no_output_____# Calculating the cluster centroids and variance # from kmeans
centroids, variance = vq.kmeans(data, 3)_____no_output_____# The identified variable contains the information
# we need to separate the points in clusters
# based on the vq function.
identified, distance = vq.vq(data, centroids)_____no_output_____# Retrieving coordinates for points in each vq # identified core
vqc1 = data[identified == 0]
vqc2 = data[identified == 1]
vqc3 = data[identified == 2]_____no_output_____%matplotlib inline_____no_output_____%load plot_clustering.py_____no_output_____plot_kmeans_clustering_results(c1, c2, c3, vqc1, vqc2, vqc3)
plt.show()_____no_output_____

### Iris Example_____no_output_____

iris.feature_names_____no_output_____iris.target_____no_output_____X = iris.data
print(X.shape)(150, 4)
# Creating Data
c1 = X[iris.target == 0]
c2 = X[iris.target == 1]
c3 = X[iris.target == 2]_____no_output_____print(c1.shape, c2.shape, c3.shape)(50, 4) (50, 4) (50, 4)
data = np.vstack([c1, c2, c3])_____no_output_____# Calculating the cluster centroids and variance # from kmeans
centroids, variance = vq.kmeans(data, 3)_____no_output_____identified, distance = vq.vq(data, centroids)_____no_output_____vqc1 = data[identified == 0]
vqc2 = data[identified == 1]
vqc3 = data[identified == 2]_____no_output_____from itertools import combinations

for cols in combinations(range(4), 2):
    print("Features: {} - {}".format(iris.feature_names[cols[0]].title(),
                                     iris.feature_names[cols[1]].title()))
    plot_kmeans_clustering_results(c1[:,cols], c2[:,cols], c3[:,cols],
                                   vqc1[:,cols], vqc2[:,cols], vqc3[:,cols])
    plt.show()Features: Sepal Length (Cm) - Sepal Width (Cm)

## Note about `kmeans` in `scipy.cluster`_____no_output_____In `scipy.cluster` we have **two** routines to divide data into clusters using the **k-means technique**:

* `kmeans`;
* `kmeans2`.

They correspond to two different implementations.

The former has a very simple syntax:

       kmeans(obs, k_or_guess, iter=20, thresh=1e-05)

The obs parameter is an `ndarray` with the data we wish to cluster.

If the dimensions of the array are m x n, the algorithm interprets this data as m points in the n-dimensional Euclidean space.

If we know the number of clusters in which this data should be divided, we enter so with `the k_or_guess` option.

**Note**: The data we pass to kmeans need to be *normalized* with the `whiten` routine.

The second function is much more flexible, as its syntax indicates:

       kmeans2(data, k, iter=10, thresh=1e-05, minit='random', missing='warn')

The `data` and `k` parameters are the same as `obs` and `k_or_guess`, respectively. The difference in this routine is the possibility of choosing among different **initialization algorithms**, hence providing us with the possibility to speed up the process and use fewer resources if we know some properties of our data.

We do so by passing to the `minit` parameter, one of the strings such as:

* `'random'` (initialization centroids are constructed randomly using a Gaussian);
* `'points'` (initialization is done by choosing points belonging to our data)- **Kmedoids**;
* `'uniform'` (if we prefer uniform distribution to Gaussian).

In case we would like to provide the initialization centroids ourselves with the `k` parameter, we must indicate our choice to the algorithm by passing 'matrix' to the minit option as well._____no_output_____## Machine Learning with scikit-learn_____no_output_____### What is scikit-learn?

_____no_output_____

from sklearn.datasets import make_blobs
blobs, classes = make_blobs(500, centers=3)

type(blobs), type(classes)_____no_output_____blobs.shape_____no_output_____

### Plotting the (randomly generated) data

#### Scatter Plot_____no_output_____

import matplotlib.pyplot as plt

f, ax = plt.subplots(figsize=(7.5, 7.5))
rgb = np.array(['r', 'g', 'b'])

ax.scatter(blobs[:, 0], blobs[:, 1], color=rgb[classes])
ax.set_title("Blobs")_____no_output_____

Now we can use **KMeans** to find the centers of these clusters.

(In this example, we'll pretend we know that there are three centers)_____no_output_____

from sklearn.cluster import KMeans

kmean = KMeans(n_clusters=3)
kmean.fit(blobs)
kmean.cluster_centers______no_output_____f, ax = plt.subplots(figsize=(7.5, 7.5))
ax.scatter(blobs[:, 0], blobs[:, 1], color=rgb[classes])
ax.scatter(kmean.cluster_centers_[:, 0],
           kmean.cluster_centers_[:, 1], marker='*', s=250,
           color='black', label='Centers')
ax.set_title("Blobs")
ax.legend(loc='best')

plt.show()_____no_output_____# Clustering Results
kmean.labels_[:5]_____no_output_____# Expected Results
classes[:5]

## Please note that KMeans does not know in advance cluster ids, thus it cannot assign the same as in `classes`.
## In any case, please note that same elements correspond to the same numbers (IDs)_____no_output_____

The `transform` function is quite useful in the sense that it will output the distance between
each point and centroid:_____no_output_____

kmean.transform(blobs)[:5]_____no_output_____

## Classifying Iris Dataset with scikit-learn_____no_output_____

kmean = KMeans(n_clusters=3)
kmean.fit(iris.data)
kmean.cluster_centers______no_output_____kmean.cluster_centers_.shape_____no_output_____
X = iris.data
for cols in combinations(range(4), 2):
    f, ax = plt.subplots(figsize=(7.5, 7.5))
    ax.scatter(X[:, cols[0]], X[:, cols[1]], color=rgb[iris.target])
    ax.scatter(kmean.cluster_centers_[:, cols[0]],
               kmean.cluster_centers_[:, cols[1]], marker='*', s=250,
               color='black', label='Centers')
    feature_x = iris.feature_names[cols[0]]
    feature_y = iris.feature_names[cols[1]]
    ax.set_title("Features: {} vs {}".format(feature_x.title(),
                                            feature_y.title()))
    ax.set_xlabel(feature_x)
    ax.set_ylabel(feature_y)
    ax.legend(loc='best')

    plt.show()_____no_output_____


# Notebook from goncalo-andrade/nrpytutorial
Path: Tutorial-BSSN_time_evolution-BSSN_gauge_RHSs.ipynb

# BSSN Time-Evolution Equations for the Gauge Fields $\alpha$ and $\beta^i$

## Authors: Zach Etienne & Terrence Pierre Jacques
### Formatting improvements courtesy Brandon Clark

[comment]: <> (Abstract: TODO, or make the introduction an abstract and addiotnal notes section, and write a new Introduction)

**Notebook Status:**  Validated

**Validation Notes:** All expressions generated in this module have been validated against a trusted code (the original NRPy+/SENR code, which itself was validated against [Baumgarte's code](https://arxiv.org/abs/1211.6632)).

### NRPy+ Source Code for this module: [BSSN/BSSN_gauge_RHSs.py](../edit/BSSN/BSSN_gauge_RHSs.py)

## Introduction:
This tutorial notebook constructs SymPy expressions for the right-hand sides of the time-evolution equations for the gauge fields $\alpha$ (the lapse, governing how much proper time elapses at each point between one timestep in a 3+1 solution to Einstein's equations and the next) and $\beta^i$ (the shift, governing how much proper distance numerical grid points move from one timestep in a 3+1 solution to Einstein's equations and the next).

Though we are completely free to choose gauge conditions (i.e., free to choose the form of the right-hand sides of the gauge time evolution equations), very few have been found robust in the presence of (puncture) black holes. So we focus here only on a few of the most stable choices.

_____no_output_____

# Table of Contents
$$\label{toc}$$

This notebook is organized as follows

1. [Step 1](#initializenrpy): Initialize needed Python/NRPy+ modules
1. [Step 2](#lapseconditions): Right-hand side of $\partial_t \alpha$
    1. [Step 2.a](#onepluslog): $1+\log$ lapse
    1. [Step 2.b](#harmonicslicing): Harmonic slicing
    1. [Step 2.c](#frozen): Frozen lapse
    1. [Step 2.d](#statictrumpet_onepluslog): Alternative 1+log condition for Static Trumpet initial data
1. [Step 3](#shiftconditions): Right-hand side of $\partial_t \beta^i$: Second-order Gamma-driving shift conditions
    1. [Step 3.a](#origgammadriving): Original, non-covariant Gamma-driving shift condition
    1. [Step 3.b](#covgammadriving): [Brown](https://arxiv.org/abs/0902.3652)'s suggested covariant Gamma-driving shift condition
        1. [Step 3.b.i](#partial_beta): The right-hand side of the $\partial_t \beta^i$ equation
        1. [Step 3.b.ii](#partial_upper_b): The right-hand side of the $\partial_t B^i$ equation
    1. [Step 3.c](#statictrumpet_nonadvecgammadriving): Non-advecting Gamma-driving shift condition (used for evolving "Static Trumpet" initial data)
1. [Step 4](#rescale): Rescale right-hand sides of BSSN gauge equations
1. [Step 5](#code_validation): Code Validation against `BSSN.BSSN_gauge_RHSs` NRPy+ module
1. [Step 6](#latex_pdf_output): Output this notebook to $\LaTeX$-formatted PDF file_____no_output_____

# Step 1: Initialize needed Python/NRPy+ modules \[Back to [top](#toc)\]
$$\label{initializenrpy}$$

Let's start by importing all the needed modules from Python/NRPy+:_____no_output_____

# Step 1: Import all needed modules from NRPy+:
import sympy as sp                # SymPy: The Python computer algebra package upon which NRPy+ depends
import NRPy_param_funcs as par    # NRPy+: Parameter interface
import grid as gri                # NRPy+: Functions having to do with numerical grids
import indexedexp as ixp          # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import reference_metric as rfm    # NRPy+: Reference metric support
import BSSN.BSSN_quantities as Bq # NRPy+: Computes useful BSSN quantities
import BSSN.BSSN_RHSs as Brhs     # NRPy+: Constructs BSSN right-hand-side expressions
import sys                        # Standard Python modules for multiplatform OS-level functions

# Step 1.c: Declare/initialize parameters for this module
thismodule = "BSSN_gauge_RHSs"
par.initialize_param(par.glb_param("char", thismodule, "LapseEvolutionOption", "OnePlusLog"))
par.initialize_param(par.glb_param("char", thismodule, "ShiftEvolutionOption", "GammaDriving2ndOrder_Covariant"))

# Step 1.d: Set spatial dimension (must be 3 for BSSN, as BSSN is
#           a 3+1-dimensional decomposition of the general
#           relativistic field equations)
DIM = 3

# Step 1.e: Given the chosen coordinate system, set up
#           corresponding reference metric and needed
#           reference metric quantities
# The following function call sets up the reference metric
#    and related quantities, including rescaling matrices ReDD,
#    ReU, and hatted quantities.
rfm.reference_metric()

# Step 1.f: Define BSSN scalars & tensors (in terms of rescaled BSSN quantities)
import BSSN.BSSN_quantities as Bq
Bq.BSSN_basic_tensors()
Bq.betaU_derivs()

import BSSN.BSSN_RHSs as Brhs
Brhs.BSSN_RHSs()_____no_output_____

# Step 2: Right-hand side of $\partial_t \alpha$ \[Back to [top](#toc)\]
$$\label{lapseconditions}$$_____no_output_____

## Step 2.a: $1+\log$ lapse \[Back to [top](#toc)\]
$$\label{onepluslog}$$

The [$1=\log$ lapse condition](https://arxiv.org/abs/gr-qc/0206072) is a member of the [Bona-Masso family of lapse choices](https://arxiv.org/abs/gr-qc/9412071), which has the desirable property of singularity avoidance. As is common (e.g., see [Campanelli *et al* (2005)](https://arxiv.org/abs/gr-qc/0511048)), we make the replacement $\partial_t \to \partial_0 = \partial_t - \beta^i \partial_i$ to ensure lapse characteristics advect with the shift. The bracketed term in the $1+\log$ lapse condition below encodes the shift advection term:

\begin{align}
\partial_0 \alpha &= -2 \alpha K \\
\implies \partial_t \alpha &= \left[\beta^i \partial_i \alpha\right] - 2 \alpha K
\end{align}_____no_output_____

# Step 2.a: The 1+log lapse condition:
#   \partial_t \alpha = \beta^i \alpha_{,i} - 2*\alpha*K
# First import expressions from BSSN_quantities
cf    = Bq.cf
trK   = Bq.trK
alpha = Bq.alpha
betaU = Bq.betaU

# Implement the 1+log lapse condition
if par.parval_from_str(thismodule+"::LapseEvolutionOption") == "OnePlusLog":
    alpha_rhs = -2*alpha*trK
    alpha_dupD = ixp.declarerank1("alpha_dupD")
    for i in range(DIM):
        alpha_rhs += betaU[i]*alpha_dupD[i]_____no_output_____

## Step 2.b: Harmonic slicing \[Back to [top](#toc)\]
$$\label{harmonicslicing}$$

As defined on Pg 2 of https://arxiv.org/pdf/gr-qc/9902024.pdf , this is given by

$$
\partial_t \alpha = \partial_t e^{6 \phi} = 6 e^{6 \phi} \partial_t \phi
$$

If

$$\text{cf} = W = e^{-2 \phi},$$

then

$$
6 e^{6 \phi} \partial_t \phi = 6 W^{-3} \partial_t \phi.
$$

However,
$$
\partial_t \phi = -\partial_t \text{cf} / (2 \text{cf})$$

(as described above), so if `cf`$=W$, then
\begin{align}
\partial_t \alpha &= 6 e^{6 \phi} \partial_t \phi \\
&= 6 W^{-3} \left(-\frac{\partial_t W}{2 W}\right) \\
&= -3 \text{cf}^{-4} \text{cf}\_\text{rhs}
\end{align}

**Exercise to students: Implement Harmonic slicing for `cf`$=\chi$** _____no_output_____

# Step 2.b: Implement the harmonic slicing lapse condition
if par.parval_from_str(thismodule+"::LapseEvolutionOption") == "HarmonicSlicing":
    if par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "W":
        alpha_rhs = -3*cf**(-4)*Brhs.cf_rhs
    elif par.parval_from_str("BSSN.BSSN_quantities::EvolvedConformalFactor_cf") == "phi":
        alpha_rhs = 6*sp.exp(6*cf)*Brhs.cf_rhs
    else:
        print("Error LapseEvolutionOption==HarmonicSlicing unsupported for EvolvedConformalFactor_cf!=(W or phi)")
        sys.exit(1)_____no_output_____

## Step 2.c: Frozen lapse \[Back to [top](#toc)\]
$$\label{frozen}$$

This slicing condition is given by
$$\partial_t \alpha = 0,$$

which is rarely a stable lapse condition._____no_output_____

# Step 2.c: Frozen lapse
#    \partial_t \alpha = 0
if par.parval_from_str(thismodule+"::LapseEvolutionOption") == "Frozen":
    alpha_rhs = sp.sympify(0)_____no_output_____

## Step 2.d: Alternative $1+\log$ lapse for Static Trumpet initial data \[Back to [top](#toc)\]
$$\label{statictrumpet_onepluslog}$$

An alternative to the standard 1+log condition to be used with Static Trumpet initial data, the lapse is evolved according to a
condition consistent with staticity, given by equation 67 in [Ruchlin, Etienne, & Baumgarte (2018)](https://arxiv.org/pdf/1712.07658.pdf)

\begin{align}
\partial_0 \alpha &= -\alpha(1 - \alpha) K \\
\implies \partial_t \alpha &= \left[\beta^i \partial_i \alpha\right] -\alpha(1 - \alpha) K
\end{align}_____no_output_____

# Step 2.d: Alternative 1+log lapse condition:
#   \partial_t \alpha = \beta^i \alpha_{,i} -\alpha*(1 - \alpha)*K

# Implement the alternative 1+log lapse condition
if par.parval_from_str(thismodule+"::LapseEvolutionOption") == "OnePlusLogAlt":
    alpha_rhs = -alpha*(1 - alpha)*trK
    alpha_dupD = ixp.declarerank1("alpha_dupD")
    for i in range(DIM):
        alpha_rhs += betaU[i]*alpha_dupD[i]_____no_output_____

# Step 3: Right-hand side of $\partial_t \beta^i$: Second-order Gamma-driving shift conditions \[Back to [top](#toc)\]
$$\label{shiftconditions}$$

The motivation behind Gamma-driving shift conditions are well documented in the book [*Numerical Relativity* by Baumgarte & Shapiro](https://www.amazon.com/Numerical-Relativity-Einsteins-Equations-Computer/dp/052151407X/).

## Step 3.a: Original, non-covariant Gamma-driving shift condition \[Back to [top](#toc)\]
$$\label{origgammadriving}$$

**Option 1: Non-Covariant, Second-Order Shift**

We adopt the [*shifting (i.e., advecting) shift*](https://arxiv.org/abs/gr-qc/0605030) non-covariant, second-order shift condition:
\begin{align}
\partial_0 \beta^i &= B^{i} \\
\partial_0 B^i &= \frac{3}{4} \partial_{0} \bar{\Lambda}^{i} - \eta B^{i} \\
\implies \partial_t \beta^i &= \left[\beta^j \partial_j \beta^i\right] + B^{i} \\
\partial_t B^i &= \left[\beta^j \partial_j B^i\right] + \frac{3}{4} \partial_{0} \bar{\Lambda}^{i} - \eta B^{i},
\end{align}
where $\eta$ is the shift damping parameter, and $\partial_{0} \bar{\Lambda}^{i}$ in the right-hand side of the $\partial_{0} B^{i}$ equation is computed by adding $\beta^j \partial_j \bar{\Lambda}^i$ to the right-hand side expression given for $\partial_t \bar{\Lambda}^i$ in the BSSN time-evolution equations as listed [here](Tutorial-BSSN_formulation.ipynb), so no explicit time dependence occurs in the right-hand sides of the BSSN evolution equations and the Method of Lines can be applied directly._____no_output_____

# Step 3.a: Set \partial_t \beta^i
# First import expressions from BSSN_quantities
BU         = Bq.BU
betU       = Bq.betU
betaU_dupD = Bq.betaU_dupD
# Define needed quantities
beta_rhsU = ixp.zerorank1()
B_rhsU = ixp.zerorank1()
if par.parval_from_str(thismodule+"::ShiftEvolutionOption") == "GammaDriving2ndOrder_NoCovariant":
    # Step 3.a.i: Compute right-hand side of beta^i
    # *  \partial_t \beta^i = \beta^j \beta^i_{,j} + B^i
    for i in range(DIM):
        beta_rhsU[i] += BU[i]
        for j in range(DIM):
            beta_rhsU[i] += betaU[j]*betaU_dupD[i][j]
    # Compute right-hand side of B^i:
    eta = par.Cparameters("REAL", thismodule, ["eta"],2.0)

    # Step 3.a.ii: Compute right-hand side of B^i
    # *  \partial_t B^i     = \beta^j B^i_{,j} + 3/4 * \partial_0 \Lambda^i - eta B^i
    # Step 3.a.iii: Define BU_dupD, in terms of derivative of rescaled variable \bet^i
    BU_dupD = ixp.zerorank2()
    betU_dupD = ixp.declarerank2("betU_dupD","nosym")
    for i in range(DIM):
        for j in range(DIM):
            BU_dupD[i][j] = betU_dupD[i][j]*rfm.ReU[i] + betU[i]*rfm.ReUdD[i][j]

    # Step 3.a.iv: Compute \partial_0 \bar{\Lambda}^i = (\partial_t - \beta^i \partial_i) \bar{\Lambda}^j
    Lambdabar_partial0 = ixp.zerorank1()
    for i in range(DIM):
        Lambdabar_partial0[i] = Brhs.Lambdabar_rhsU[i]
    for i in range(DIM):
        for j in range(DIM):
            Lambdabar_partial0[j] += -betaU[i]*Brhs.LambdabarU_dupD[j][i]

    # Step 3.a.v: Evaluate RHS of B^i:
    for i in range(DIM):
        B_rhsU[i] += sp.Rational(3,4)*Lambdabar_partial0[i] - eta*BU[i]
        for j in range(DIM):
            B_rhsU[i] += betaU[j]*BU_dupD[i][j]_____no_output_____

## Step 3.b: [Brown](https://arxiv.org/abs/0902.3652)'s suggested covariant Gamma-driving shift condition \[Back to [top](#toc)\]
$$\label{covgammadriving}$$_____no_output_____

### Step 3.b.i: The right-hand side of the $\partial_t \beta^i$ equation \[Back to [top](#toc)\]
$$\label{partial_beta}$$

This is [Brown's](https://arxiv.org/abs/0902.3652) suggested formulation (Eq. 20b; note that Eq. 20a is the same as our lapse condition, as $\bar{D}_j \alpha = \partial_j \alpha$ for scalar $\alpha$):
$$\partial_t \beta^i = \left[\beta^j \bar{D}_j \beta^i\right] + B^{i}$$
Based on the definition of covariant derivative, we have
$$
\bar{D}_{j} \beta^{i} = \beta^i_{,j} + \bar{\Gamma}^i_{mj} \beta^m,
$$
so the above equation becomes
\begin{align}
\partial_t \beta^i &= \left[\beta^j \left(\beta^i_{,j} + \bar{\Gamma}^i_{mj} \beta^m\right)\right] + B^{i}\\
&= {\underbrace {\textstyle \beta^j \beta^i_{,j}}_{\text{Term 1}}} +
{\underbrace {\textstyle \beta^j \bar{\Gamma}^i_{mj} \beta^m}_{\text{Term 2}}} +
{\underbrace {\textstyle B^i}_{\text{Term 3}}}
\end{align}_____no_output_____

# Step 3.b: The right-hand side of the \partial_t \beta^i equation
if par.parval_from_str(thismodule+"::ShiftEvolutionOption") == "GammaDriving2ndOrder_Covariant":
    # Step 3.b Option 2: \partial_t \beta^i = \left[\beta^j \bar{D}_j \beta^i\right] + B^{i}
    # First we need GammabarUDD, defined in Bq.gammabar__inverse_and_derivs()
    Bq.gammabar__inverse_and_derivs()
    GammabarUDD = Bq.GammabarUDD
    # Then compute right-hand side:
    # Term 1: \beta^j \beta^i_{,j}
    for i in range(DIM):
        for j in range(DIM):
            beta_rhsU[i] += betaU[j]*betaU_dupD[i][j]

    # Term 2: \beta^j \bar{\Gamma}^i_{mj} \beta^m
    for i in range(DIM):
        for j in range(DIM):
            for m in range(DIM):
                beta_rhsU[i] += betaU[j]*GammabarUDD[i][m][j]*betaU[m]
    # Term 3: B^i
    for i in range(DIM):
        beta_rhsU[i] += BU[i]_____no_output_____

### Step 3.b.ii: The right-hand side of the $\partial_t B^i$ equation \[Back to [top](#toc)\]
$$\label{partial_upper_b}$$

$$\partial_t B^i = \left[\beta^j \bar{D}_j B^i\right] + \frac{3}{4}\left( \partial_t \bar{\Lambda}^{i} - \beta^j \bar{D}_j \bar{\Lambda}^{i} \right) - \eta B^{i}$$

Based on the definition of covariant derivative, we have for vector $V^i$
$$
\bar{D}_{j} V^{i} = V^i_{,j} + \bar{\Gamma}^i_{mj} V^m,
$$
so the above equation becomes
\begin{align}
\partial_t B^i &= \left[\beta^j \left(B^i_{,j} + \bar{\Gamma}^i_{mj} B^m\right)\right] + \frac{3}{4}\left[ \partial_t \bar{\Lambda}^{i} - \beta^j \left(\bar{\Lambda}^i_{,j} + \bar{\Gamma}^i_{mj} \bar{\Lambda}^m\right) \right] - \eta B^{i} \\
&= {\underbrace {\textstyle \beta^j B^i_{,j}}_{\text{Term 1}}} +
{\underbrace {\textstyle \beta^j \bar{\Gamma}^i_{mj} B^m}_{\text{Term 2}}} +
{\underbrace {\textstyle \frac{3}{4}\partial_t \bar{\Lambda}^{i}}_{\text{Term 3}}} -
{\underbrace {\textstyle \frac{3}{4}\beta^j \bar{\Lambda}^i_{,j}}_{\text{Term 4}}} -
{\underbrace {\textstyle \frac{3}{4}\beta^j \bar{\Gamma}^i_{mj} \bar{\Lambda}^m}_{\text{Term 5}}} -
{\underbrace {\textstyle \eta B^i}_{\text{Term 6}}}
\end{align}_____no_output_____

if par.parval_from_str(thismodule+"::ShiftEvolutionOption") == "GammaDriving2ndOrder_Covariant":
    # Step 3.c: Covariant option:
    #  \partial_t B^i = \beta^j \bar{D}_j B^i
    #               + \frac{3}{4} ( \partial_t \bar{\Lambda}^{i} - \beta^j \bar{D}_j \bar{\Lambda}^{i} )
    #               - \eta B^{i}
    #                 = \beta^j B^i_{,j} + \beta^j \bar{\Gamma}^i_{mj} B^m
    #               + \frac{3}{4}[ \partial_t \bar{\Lambda}^{i}
    #                            - \beta^j (\bar{\Lambda}^i_{,j} + \bar{\Gamma}^i_{mj} \bar{\Lambda}^m)]
    #               - \eta B^{i}
    # Term 1, part a: First compute B^i_{,j} using upwinded derivative
    BU_dupD = ixp.zerorank2()
    betU_dupD = ixp.declarerank2("betU_dupD","nosym")
    for i in range(DIM):
        for j in range(DIM):
            BU_dupD[i][j] = betU_dupD[i][j]*rfm.ReU[i] + betU[i]*rfm.ReUdD[i][j]
    # Term 1: \beta^j B^i_{,j}
    for i in range(DIM):
        for j in range(DIM):
            B_rhsU[i] += betaU[j]*BU_dupD[i][j]
    # Term 2: \beta^j \bar{\Gamma}^i_{mj} B^m
    for i in range(DIM):
        for j in range(DIM):
            for m in range(DIM):
                B_rhsU[i] += betaU[j]*GammabarUDD[i][m][j]*BU[m]
    # Term 3: \frac{3}{4}\partial_t \bar{\Lambda}^{i}
    for i in range(DIM):
        B_rhsU[i] += sp.Rational(3,4)*Brhs.Lambdabar_rhsU[i]
    # Term 4: -\frac{3}{4}\beta^j \bar{\Lambda}^i_{,j}
    for i in range(DIM):
        for j in range(DIM):
            B_rhsU[i] += -sp.Rational(3,4)*betaU[j]*Brhs.LambdabarU_dupD[i][j]
    # Term 5: -\frac{3}{4}\beta^j \bar{\Gamma}^i_{mj} \bar{\Lambda}^m
    for i in range(DIM):
        for j in range(DIM):
            for m in range(DIM):
                B_rhsU[i] += -sp.Rational(3,4)*betaU[j]*GammabarUDD[i][m][j]*Bq.LambdabarU[m]
    # Term 6: - \eta B^i
    # eta is a free parameter; we declare it here:
    eta = par.Cparameters("REAL", thismodule, ["eta"],2.0)
    for i in range(DIM):
        B_rhsU[i] += -eta*BU[i]_____no_output_____

## Step 3.c: Non-advecting Gamma-driving shift condition (used for evolving "Static Trumpet" initial data) \[Back to [top](#toc)\]
$$\label{statictrumpet_nonadvecgammadriving}$$

For the shift vector evolution equation, we desire only that the right-hand sides vanish analytically (although numerical error is expected to result in specious evolution). To this end, we adopt the nonadvecting Gamma-driver condition, given by equations 68a and 68b in [Ruchlin, Etienne, & Baumgarte (2018)](https://arxiv.org/pdf/1712.07658.pdf)

\begin{align}
\partial_t \beta^i &= B^{i} \\
\partial_t B^i &= \frac{3}{4} \partial_{t} \bar{\Lambda}^{i} - \eta B^{i},
\end{align}_____no_output_____

# Step 3.c: Set \partial_t \beta^i

if par.parval_from_str(thismodule+"::ShiftEvolutionOption") == "NonAdvectingGammaDriving":
    # Step 3.c.i: Compute right-hand side of beta^i
    # *  \partial_t \beta^i = B^i
    for i in range(DIM):
        beta_rhsU[i] += BU[i]

    # Compute right-hand side of B^i:
    eta = par.Cparameters("REAL", thismodule, ["eta"],2.0)

    # Step 3.c.ii: Compute right-hand side of B^i
    # *  \partial_t B^i     = 3/4 * \partial_t \Lambda^i - eta B^i
    # Step 3.c.iii: Evaluate RHS of B^i:
    for i in range(DIM):
        B_rhsU[i] += sp.Rational(3,4)*Brhs.Lambdabar_rhsU[i] - eta*BU[i]
_____no_output_____

# Step 4: Rescale right-hand sides of BSSN gauge equations \[Back to [top](#toc)\]
$$\label{rescale}$$

Next we rescale the right-hand sides of the BSSN equations so that the evolved variables are $\left\{h_{i j},a_{i j},\text{cf}, K, \lambda^{i}, \alpha, \mathcal{V}^i, \mathcal{B}^i\right\}$_____no_output_____

# Step 4: Rescale the BSSN gauge RHS quantities so that the evolved
#         variables may remain smooth across coord singularities
vet_rhsU    = ixp.zerorank1()
bet_rhsU    = ixp.zerorank1()
for i in range(DIM):
    vet_rhsU[i]    =   beta_rhsU[i] / rfm.ReU[i]
    bet_rhsU[i]    =      B_rhsU[i] / rfm.ReU[i]
#print(str(Abar_rhsDD[2][2]).replace("**","^").replace("_","").replace("xx","x").replace("sin(x2)","Sin[x2]").replace("sin(2*x2)","Sin[2*x2]").replace("cos(x2)","Cos[x2]").replace("detgbaroverdetghat","detg"))
#print(str(Dbarbetacontraction).replace("**","^").replace("_","").replace("xx","x").replace("sin(x2)","Sin[x2]").replace("detgbaroverdetghat","detg"))
#print(betaU_dD)
#print(str(trK_rhs).replace("xx2","xx3").replace("xx1","xx2").replace("xx0","xx1").replace("**","^").replace("_","").replace("sin(xx2)","Sinx2").replace("xx","x").replace("sin(2*x2)","Sin2x2").replace("cos(x2)","Cosx2").replace("detgbaroverdetghat","detg"))
#print(str(bet_rhsU[0]).replace("xx2","xx3").replace("xx1","xx2").replace("xx0","xx1").replace("**","^").replace("_","").replace("sin(xx2)","Sinx2").replace("xx","x").replace("sin(2*x2)","Sin2x2").replace("cos(x2)","Cosx2").replace("detgbaroverdetghat","detg"))_____no_output_____

# Step 5: Code Validation against `BSSN.BSSN_gauge_RHSs` NRPy+ module \[Back to [top](#toc)\]
$$\label{code_validation}$$

Here, as a code validation check, we verify agreement in the SymPy expressions for the RHSs of the BSSN gauge equations between

1. this tutorial and
2. the NRPy+ [BSSN.BSSN_gauge_RHSs](../edit/BSSN/BSSN_gauge_RHSs.py) module.

By default, we analyze the RHSs in Spherical coordinates and with the covariant Gamma-driving second-order shift condition, though other coordinate systems & gauge conditions may be chosen._____no_output_____

# Step 5: We already have SymPy expressions for BSSN gauge RHS expressions
#         in terms of other SymPy variables. Even if we reset the
#         list of NRPy+ gridfunctions, these *SymPy* expressions for
#         BSSN RHS variables *will remain unaffected*.
#
#         Here, we will use the above-defined BSSN gauge RHS expressions
#         to validate against the same expressions in the
#         BSSN/BSSN_gauge_RHSs.py file, to ensure consistency between
#         this tutorial and the module itself.
#
# Reset the list of gridfunctions, as registering a gridfunction
#   twice will spawn an error.
gri.glb_gridfcs_list = []

# Step 5.a: Call the BSSN_gauge_RHSs() function from within the
#           BSSN/BSSN_gauge_RHSs.py module,
#           which should generate exactly the same expressions as above.
import BSSN.BSSN_gauge_RHSs as Bgrhs
par.set_parval_from_str("BSSN.BSSN_gauge_RHSs::ShiftEvolutionOption","GammaDriving2ndOrder_Covariant")
Bgrhs.BSSN_gauge_RHSs()

print("Consistency check between BSSN.BSSN_gauge_RHSs tutorial and NRPy+ module: ALL SHOULD BE ZERO.")

print("alpha_rhs - bssnrhs.alpha_rhs = " + str(alpha_rhs - Bgrhs.alpha_rhs))

for i in range(DIM):
    print("vet_rhsU["+str(i)+"] - bssnrhs.vet_rhsU["+str(i)+"] = " + str(vet_rhsU[i] - Bgrhs.vet_rhsU[i]))
    print("bet_rhsU["+str(i)+"] - bssnrhs.bet_rhsU["+str(i)+"] = " + str(bet_rhsU[i] - Bgrhs.bet_rhsU[i]))Consistency check between BSSN.BSSN_gauge_RHSs tutorial and NRPy+ module: ALL SHOULD BE ZERO.
alpha_rhs - bssnrhs.alpha_rhs = 0
vet_rhsU[0] - bssnrhs.vet_rhsU[0] = 0
bet_rhsU[0] - bssnrhs.bet_rhsU[0] = 0
vet_rhsU[1] - bssnrhs.vet_rhsU[1] = 0
bet_rhsU[1] - bssnrhs.bet_rhsU[1] = 0
vet_rhsU[2] - bssnrhs.vet_rhsU[2] = 0
bet_rhsU[2] - bssnrhs.bet_rhsU[2] = 0

# Step 6: Output this notebook to $\LaTeX$-formatted PDF file \[Back to [top](#toc)\]
$$\label{latex_pdf_output}$$

The following code cell converts this Jupyter notebook into a proper, clickable $\LaTeX$-formatted PDF file. After the cell is successfully run, the generated PDF may be found in the root NRPy+ tutorial directory, with filename
[Tutorial-BSSN_time_evolution-BSSN_gauge_RHSs.pdf](Tutorial-BSSN_time_evolution-BSSN_gauge_RHSs.pdf) (Note that clicking on this link may not work; you may need to open the PDF file through another means.)_____no_output_____

import cmdline_helper as cmd    # NRPy+: Multi-platform Python command-line interface
cmd.output_Jupyter_notebook_to_LaTeXed_PDF("Tutorial-BSSN_time_evolution-BSSN_gauge_RHSs")Created Tutorial-BSSN_time_evolution-BSSN_gauge_RHSs.tex, and compiled LaTeX file to PDF file Tutorial-BSSN_time_evolution-BSSN_gauge_RHSs.pdf


# Notebook from LasalJayawardena/NLP-Projects
Path: Topic_Modelling_Business/.ipynb_checkpoints/Topic_Modelling_Notebook-checkpoint.ipynb

## Topic Modelling For Business_____no_output_____For this project we will use the articles on the emerging trend of:

### Digital Economy

The dataset hosts 2488 articles in this topic. The summary of each topic will be used as the base for the topic models.

This project is divided in 6 tasks:

####Task 1: Introduction to the dataset
####Task 2: Data cleaning and preparation
####Task 3: Exploratory analysis with WordCloud
####Task 4: Prepare data for Topic Modelling
####Task 5: Build Topic Model
####Task 6: Visualize Model_____no_output_____###Task 1: Introduction to the dataset_____no_output_____Importing Modules_____no_output_____

# Importing modules
import pandas as pd
import os
os.chdir('..')

import numpy as np
import re, nltk, spacy, gensim

import logging
import warnings
warnings.filterwarnings('ignore')

from gensim.models.coherencemodel import CoherenceModel
from gensim.models.ldamodel import LdaModel
from gensim.corpora.dictionary import Dictionary
from numpy import array_____no_output_____# Upload .csv file
_____no_output_____# Store dataset in a Pandas Dataframe
_____no_output_____

Read dataset_____no_output_____### Task 2: Data cleaning and preparation_____no_output_____Models will be built on the column 'Abstract'.
Other columns could be used for exploratory analysis._____no_output_____

# Note the drop in number of rows_____no_output_____import re

# Remove punctuation
df['p_abstract'] =

# Convert the titles to lowercase
df['p_abstract'] =

# Print out the first rows of papers
df['p_abstract']_____no_output_____

### Task 3: Exploratory Analysis with WordCloud_____no_output_____Build wordclouds for Title and Abstract fields._____no_output_____

#For Title

# Import the wordcloud library
from wordcloud import WordCloud

# Join the different titles together.
long_string = ','.join(list(df['Title'].values))

# Generate the word cloud
wordcloud =

# Visualize the word cloud
_____no_output_____

Exercise: Build a wordcloud with top 200 words using Abstract field._____no_output_____

#For Abstract
#Print only top 200 words

# Join the different processed abstracts together.

# Generate the word cloud
wordcloud2 =

# Visualize the word cloud
_____no_output_____

Remove the commonly used words (or irrelevant according to the context) from the wordcloud_____no_output_____

## Remove STOPWORDS taking reference from previous wordcloud

from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator

## Create stopword list:
stopwords = set(STOPWORDS)
stopwords.update(["research", "economic", "study", "data", "development", "result", "analysis", "model",
                 "based", "using", "new", "market", "business", "system", "use"])

# Generate the word cloud
wordcloud3 = WordCloud

# Visualize the word cloud
wordcloud3.to_image()_____no_output_____

###Task 4: Prepare data for Topic Modelling_____no_output_____Step 1: Split sentences to words_____no_output_____

%%time
import gensim
from gensim.utils import simple_preprocess
_____no_output_____

Step 2: Build N-grams_____no_output_____

# Build the bigram and trigram models
bigram =                                                  # higher threshold fewer phrases.
trigram =
# Faster way to get a sentence formatted as a bigram or trigram
bigram_mod =
trigram_mod = _____no_output_____

Step 3: Remove Stopwords_____no_output_____

# NLTK Stop words
import nltk
nltk.download('stopwords')
from nltk.corpus import stopwords
stop_words = stopwords.words('english')
stop_words.extend(['research', 'study', 'data', 'development', 'result', 'analysis', 'model',
                 'based', 'using', 'new', 'market', 'business', 'system', 'use'])
# Define functions for stopwords, bigrams, trigrams and lemmatization
def remove_stopwords(texts):
    return
def make_bigrams(texts):
    return
def make_trigrams(texts):
    return

def lemmatization(texts, allowed_postags=['NOUN', 'ADJ', 'VERB', 'ADV']):
    """https://spacy.io/api/annotation"""
    _____no_output_____

Step 4: Lemmatize_____no_output_____

import spacy
# Remove Stop Words
data_words_nostops =
# Form Bigrams
data_words_bigrams =
# Initialize spacy 'en' model, keeping only tagger component (for efficiency)
nlp =
# Lemmatize keeping only noun, adj, vb, adv
data_lemmatized =
print_____no_output_____

Step 5: Build Corpora_____no_output_____

import gensim.corpora as corpora
# Create Dictionary
id2word =
# Create Corpus
texts =
# Term Document Frequency
corpus =
# View
print_____no_output_____

###Task 5: Build topic model_____no_output_____

# Build LDA model
lda_model = _____no_output_____from pprint import pprint
# Print the Keyword in the 10 topics
pprint(lda_model.print_topics())
doc_lda = lda_model[corpus]_____no_output_____from gensim.models import CoherenceModel

# Compute Perplexity
print('\nPerplexity: ', )  # a measure of how good the model is. Lower value is preferred.

# Compute Coherence Score
coherence_model_lda =
coherence_lda =
print('\nCoherence Score: ', )

# Compute Coherence Score using UMass
coherence_model_lda =
coherence_lda =
print('\nCoherence Score u_mass: ', )_____no_output_____

###Task 6: Visualize model_____no_output_____

# Visualize the topics
!pip install -U pyLDAvis
import pyLDAvis.gensim
_____no_output_____pyLDAvis.enable_notebook()
vis =
vis_____no_output_____

References

Liu, L., Tang, L., Dong, W., Yao, S., & Zhou, W. (2016). An overview of topic modeling and its current applications in bioinformatics. SpringerPlus, 5(1), 1608. https://doi.org/10.1186/s40064-016-3252-8)_____no_output_____


# Notebook from LeonKennedy/jupyter_space
Path: jiuqu/student_test_performance.ipynb

from odps import ODPS, options
from odps.df import agg
# from odps.inter import setup, enter, teardown
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
options.sql.use_odps2_extension = True
odps = ODPS('LTAItDVDpC9aEMXJ', 'U6iEFCZCzoPUbjou1FW45SQruxIfFt', 'Qkids_V2_dev')
            #endpoint='**your-end-point**')
# %load_ext odps
# setup('LTAItDVDpC9aEMXJ', 'U6iEFCZCzoPUbjou1FW45SQruxIfFt', 'Qkids_V2_dev')
# room = enter()
# odps = room.odps_____no_output_____student_process_test = odps.get_table("qkids_v2_dev.dwd_student_process_test_synthetic").to_df()
vip_students = odps.get_table("dwd_students").to_df()
vips = vip_students[vip_students.vip==1].user_id.to_pandas()
mean_process_test = odps.get_table("qkids_v2_dev.dws_stduent_process_test_synthetic_mean_by_lesson_category").to_df().to_pandas()_____no_output_____vips_____no_output_____mean_process_test.head(10)_____no_output_____student_process_test.head(10)_____no_output_____# 查看不同个类型的整体学生评分
category = "grammar"
cat_df = mean_process_test.loc[(mean_process_test.category == category) & (mean_process_test.ordered_id > 0)]
f = plt.figure(figsize=(18,12))
# f.set_title(category)
plt.title(category)
plt.scatter(cat_df.ordered_id, cat_df.avg_score)_____no_output_____def trim_axs(axs, N):
    """little helper to massage the axs list to have correct length..."""
    axs = axs.flat
    for ax in axs[N:]:
        ax.remove()
    return axs[:N]
lessons = list(range(1,121))
def hist_33(category):
    fig1, axs = plt.subplots(3, 3, figsize=(16,12), constrained_layout=True)
    axs = trim_axs(axs, 9)
    for ax, user_id in zip(axs, vips.sample(n=9)):
        ax.set_title('user_id=%s' % str(user_id))
        ax.set_ylim(-1.5, 1)
        ax.set_xlim(lessons[0], lessons[-1])
        user = data[(student_process_test.user_id==user_id) & (student_process_test.category == category) & (student_process_test.lesson_id.isin(lessons))].to_pandas()
        user = user.set_index('lesson_id')
        mean_df = mean_process_test.loc[(mean_process_test.category==category) & (mean_process_test.lesson_id.isin(lessons))].set_index('lesson_id')
        mean_std = mean_df['std_val'].apply(lambda x:  np.nan if x == 0 else x)
        zof = (user.val - mean_df.avg_score)/mean_std
        ax.bar(zof.index, zof.values)_____no_output_____category = "pronunciation"
hist_33(category)_____no_output_____def show_student(user_id):
    lessons = list(range(1,121))
    user = student_process_test[(student_process_test.user_id == user_id) & (student_process_test.lesson_id > 0) & (student_process_test.lesson_id < 121)].to_pandas()
    fig1, axs = plt.subplots(3, 3, figsize=(16,12), constrained_layout=True)
    axs = trim_axs(axs, 9)
    for ax, category in zip(axs, user.category.unique()):
        ax.set_title(f"category={category}")
        ax.set_xlim([lessons[0], lessons[-1]])
        user_category = user.loc[user.category == category]
        user_category = user_category.set_index('lesson_id')
        category_mean_df = mean_process_test.loc[(mean_process_test.category==category) & (mean_process_test.lesson_id.isin(lessons))].set_index('lesson_id')
        category_mean_df_std = category_mean_df['std_val'].apply(lambda x:  np.nan if x == 0 else x)
        zof = (user_category.val - category_mean_df.avg_score)/category_mean_df_std
        ax.bar(zof.index, zof.values)
## 查看做题情况
def user_test_optines(user_id):
    user = student_process_test[(student_process_test.user_id == user_id) & (student_process_test.lesson_id > 0) & (student_process_test.lesson_id < 121)].to_pandas()
    user_cate = user.pivot(index='lesson_id', columns="category", values="val")
    return user_cate_____no_output_____user_id = int(vips.sample(1).values[0])
show_student(475669)_____no_output_____user_test_optines(475669)_____no_output_____def get_star_by_score(score, avg_score, std):
    if score == 100:
        return 3
    elif score >= avg_score:
        return 2
    elif score >= avg_score-std:
        return 1
    else:
        return 0
def culumice(category):
    fig1, axs = plt.subplots(3, 5, figsize=(24,9), constrained_layout=True)
    axs = trim_axs(axs, 15)
    lessons = list(range(1,121))
    for ax, user_id in zip(axs, vips.sample(n=80)):
        ax.set_title('user_id=%s, category=%s' % (str(user_id), category))
        ax.set_xlim((0, 120))
        user = student_process_test[(student_process_test.user_id==user_id) & (student_process_test.category == category)].to_pandas()
        user = user.loc[user.lesson_id.isin(lessons)]
        if len(user) == 0:
            continue
        user = user.set_index('lesson_id')
        mean_df = mean_process_test.loc[mean_process_test.category==category].set_index('lesson_id')
        complexs = user.join(mean_df, lsuffix="_same")
        complexs['star'] =  complexs.apply(lambda x: get_star_by_score(x.val, x.avg_score, x.std_val), axis=1)
        complexs['mean_star'] = 2
        complexs['max_star'] = 3
        output = complexs[['mean_star', 'star', 'max_star']].cumsum()
        ax.plot(output.index, output.mean_star, label='common level')
        ax.plot(output.index, output.star, label="your level")
        ax.plot(output.index, output.max_star, label="perfect")
        ax.legend()_____no_output_____category = "reading"
culumice(category)_____no_output_____def culumice_user(user_id):
    user = student_process_test[(student_process_test.user_id == user_id) & (student_process_test.lesson_id > 0) & (student_process_test.lesson_id < 121)].to_pandas()
    fig1, axs = plt.subplots(2, 4, figsize=(20,6), constrained_layout=True)
    axs = trim_axs(axs, 8)
    lessons = list(range(1,121))
    for ax, category in zip(axs, user.category.unique()):
        ax.set_title('user_id=%s, category=%s' % (str(user_id), category))
        ax.set_xlim((0, 120))

        user_category = user.loc[user.category == category]
        if len(user) == 0:
            continue
        user_category = user_category.set_index('lesson_id')

        category_mean_df = mean_process_test.loc[(mean_process_test.category==category) & (mean_process_test.lesson_id.isin(lessons))].set_index('lesson_id')
        complexs = user.join(category_mean_df, lsuffix="_same")
        complexs['star'] =  complexs.apply(lambda x: get_star_by_score(x.val, x.avg_score, x.std_val), axis=1)
        complexs['mean_star'] = 2
        complexs['max_star'] = 3
        output = complexs[['mean_star', 'star', 'max_star']].cumsum()
        ax.plot(output.index, output.mean_star, label='common level')
        ax.plot(output.index, output.star, label="your level")
        ax.plot(output.index, output.max_star, label="perfect")
        ax.legend()_____no_output_____user_id = int(vips.sample(1).values[0])
culumice_user(user_id)_____no_output_____#做题次数情况 x周是lesson1 - lesson 120
times_pdf = mean_df['lesson_id','times_mean'].to_pandas()
times_pdf = times_pdf.set_index('lesson_id')
times_pdf.plot()_____no_output_____

### 学生上课频率情况_____no_output_____

student_appointment_cnt = odps.get_table("qkids_v2_dev.dwd_student_appointment_count_w").to_df()
sac_df = student_appointment_cnt.student_appointment_cnt.value_counts().to_pandas()_____no_output_____f = plt.figure(figsize=(20,10))
plt.bar(sac_df.student_appointment_cnt.values, sac_df['count'])_____no_output_____vip_appointment_cnt = odps.get_table("qkids_v2_dev.dws_vip_appointment_cnt_w").to_df()
vac_df = vip_appointment_cnt.student_appointment_cnt.value_counts().to_pandas()
vac_df.plot(kind="bar", figsize=(28,10))_____no_output_____weeks =  list(range(201947, 201938, -1))
week_vip_appointment_cnt = vip_appointment_cnt[(vip_appointment_cnt.week_id> 201938) &(vip_appointment_cnt.week_id< 201948)].to_pandas()_____no_output_____# 每周上课次数频率统计
fig1, axs = plt.subplots(3, 3, figsize=(20,10), constrained_layout=True)
axs = trim_axs(axs, 9)
for ax, week_id in zip(axs, weeks):
    ax.set_title('week_id=%s' % str(week_id))
    data = week_vip_appointment_cnt.loc[(week_vip_appointment_cnt.week_id==week_id), 'student_appointment_cnt'].value_counts()
    ax.bar(data.index, data)_____no_output_____#  抽样学生上课情况统计
fig1, axs = plt.subplots(3, 3, figsize=(20,10), constrained_layout=True)
users = week_vip_appointment_cnt.student_id.sample(9)
axs = trim_axs(axs, 9)
for ax, user_id in zip(axs, users):
    ax.set_title('student_id=%s' % str(user_id))
    data = week_vip_appointment_cnt.loc[(week_vip_appointment_cnt.student_id==user_id)]
    ax.set_xticklabels(data.week_id)
    ax.set_ylim([0,8])
    ax.bar(data.week_id, data.student_appointment_cnt, capstyle="round", joinstyle='bevel', linestyle=":")_____no_output_____