5 Shiny
It’s now time to dig into Shiny - a system to build (amongst of things) web dashboards in R. By the end of this tutorial you’ll hopefully have everything you need to build them yourselves.
5.1 Building your first app
This is the code behind a Shiny app. Run it on your machine and see what it looks like!
library(shinydashboard)
library(shiny)
ui <- dashboardPage(
dashboardHeader(title = "A simple dashboard"),
dashboardSidebar(),
dashboardBody(
fluidRow(
box(plotOutput("first_plot", height = 250)),
box(
title = "Controls",
sliderInput("slider", "Number of observations:", 1, 100, 50)
)
)
)
)
server <- function(input, output) {
histdata <- rnorm(500)
output$first_plot <- renderPlot({
data <- histdata[sample(input$slider)]
hist(data)
})
}
shinyApp(ui, server)
So let’s break down what’s happening in the code. We have two main components: ui
and server
. ui
contains, well, the user interface: the title of the page, how the page is formatted, what it contains (in this case, a box of plot output, which is the histogram, and a box of controls - the slider you see).
ui
objects have three mandatory components. The first is dashboardHeader
, the title bar. The second is dashboardSidebar
, the, well, sidebar: when you have multiple tabs, it’s where you’ll identify each tab and how to represent it. Finally, we have dashboardBody
, which contains the code to actually display plots or other dashboard elements, and control how those elements should be displayed.
server
contains the actual number-crunching and dashboard generation. It’s a function that takes two arguments, input
and output
: input
contains the values of any user-facing controls, while output
is used to store rendered plots or text elements or whatever you’re displaying. We can see how this comes together in our example: ui
has a plotOutput
named “first_plot”, and a sliderInput
called “slider”. server
uses the slider input (at input$slider
) to generate a plot (at output$first_plot
), and the result is a nice controllable visualisation displayed for the world to see.
5.2 User Interface design
One of the nice things about Shiny dashboards is that you can tweak their UIs to make them distinctive and fit into a more general colour scheme. A simple example, obviously, is modifying the title
in dashboardHeader
, but we can also change the overall skin and theme by adding skin = [name_of_skin]
to dashboardPage
: options are “blue”, “black”, “purple”, “green”, “red” and “yellow”. Let’s try it now:
library(shinydashboard)
library(shiny)
ui <- dashboardPage(
skin = "yellow",
dashboardHeader(title = "A simple dashboard with a custom title"),
dashboardSidebar(),
dashboardBody(
fluidRow(
box(plotOutput("first_plot", height = 250)),
box(
title = "Controls",
sliderInput("slider", "Number of observations:", 1, 100, 50)
)
)
)
)
server <- function(input, output) {
histdata <- rnorm(500)
output$first_plot <- renderPlot({
data <- histdata[sample(input$slider)]
hist(data)
})
}
shinyApp(ui, server)
I’ll be honest and say that “congealed cheez-whiz orange” isn’t my ideal colour, but there are other options to pick from. Beyond that, if you want to make big or small tweaks (and you’re interested in UI and website design) you can include custom CSS, as demonstrated here.
5.3 Custom inputs and outputs
So we’ve got our basic dashboard, and we’ve tinkered with it to give it a nice title and maybe a custom theme. Next we should probably give it real data, and experiment with customising the input and output (and adding multiple tabs.
Let’s start with integrating the patient data, since we’re familiar with that, and build a dashboard that:
- Reads in the patient data;
- Generates two histograms, one for admissions and one for releases, in different tabs;
- Lets a user select different years (and adjusts the dashboard accordingly).
We already know how to read in the patient data, so that’s easy; we’ll just put that in the server
function definition. It’ll run once when the dashboard is launched, and then just sit there waiting to be used. After that, though, we have to actually generate the plots.
For that we use renderPlot
, which runs whatever code you put in it and then returns the result (expecting it to be, well, a plot!)
So for a simple histogram of admissions data, we replace the contents of server
with:
patient_data <- read_csv(file = "https://www.ironholds.org/resources/misc/patient_data.csv")
output$admissions_plot <- renderPlot({
hist(patient_data$admitted)
})
And done. The release plot looks pretty much the same (although we’d presumably call it, say, release_plot
).
(pay attention to the admissions_plot
name. That’ll be relevant later)
Selecting the years is the next bit. As we’ve seen, a slider in the dashboard’s UI can provide a value to be plugged into the calculations that generate the plot; we can also use dropdown selectors, which are more relevant here, with selectInput
. So let’s assume the existence of a dropdown - we’ll actually define it slightly later - called admissions_selector
, and use that to subset the data we give to the plotting function:
patient_data <- read_csv(file = "https://www.ironholds.org/resources/misc/patient_data.csv")
output$admissions_plot <- renderPlot({
admissions_data <- patient_data[patient_data$year == input$admissions_selector,]
hist(admissions_data$admitted)
})
And there we have it. The use of a slider, or selector, or any other user-inputted information to dynamically adjust the dashboard, is referred to as reactivity: the expressions that do so, reactive expressions. Useful to know for debugging or googling for help!
Defining multiple tabs is the next bit. As mentioned, this is controlled through both dashboardSidebar
and dashboardBody
: the sidebar says “there is a tab, and it’s called X”, the body says “and the contents of X are…”. So we can define multiple tabs (which we’ll call admitted_hist
and released_hist
for simplicity) by replacing dashboardSidebar()
with:
dashboardSidebar(
sidebarMenu(
menuItem(text = "Patient admissions", tabName = "admitted_hist"),
menuItem(text = "Patient releases", tabName = "released_hist")
)
)
So this creates a sidebar, with a menu, featuring two tabs. if you include it in one of the dashboard examples above, you’ll see that the text
argument is what’s actually displayed: the tabName
lets us match sidebar entries with actual dashboard bodies.
On that note, let’s make the dashboard bodies, using our initial example. We know that we need two tabs, each with their own sliders and plots, and we also know the names of those sliders and plots. So, adapting the example we had of a Shiny dashboard right at the beginning of the section (but using tabItem
instead of fluidRow
, because we’re making distinct tabs), the dashboard body looks like:
dashboardBody(
tabItems(
tabItem(tabName = "admitted_hist",
box(plotOutput("admissions_plot", height = 250)),
box(
title = "Controls",
selectInput(inputId = "admissions_selector",
label = "Year:",
choices = c(2009, 2010, 2011, 2012, 2013, 2014)
)
)
),
tabItem(tabName = "released_hist",
box(plotOutput("released_plot", height = 250)),
box(
title = "Controls",
selectInput(inputId = "released_selector",
label = "Year:",
choices = c(2009, 2010, 2011, 2012, 2013, 2014)
)
)
)
)
)
And when we put it all together:
library(shinydashboard)
library(shiny)
library(readr)
# The UI
ui <- dashboardPage(
skin = "black",
dashboardHeader(title = "Admission and release data"),
dashboardSidebar(
sidebarMenu(
menuItem(text = "Patient admissions", tabName = "admitted_hist"),
menuItem(text = "Patient releases", tabName = "released_hist")
)
),
# The body of the UI
dashboardBody(
tabItems(
# Admissions tab
tabItem(tabName = "admitted_hist",
box(plotOutput("admissions_plot", height = 250)),
box(
title = "Controls",
selectInput(inputId = "admissions_selector",
label = "Year:",
choices = c(2009, 2010, 2011, 2012, 2013, 2014)
)
)
),
# Releases tab
tabItem(tabName = "released_hist",
box(plotOutput("released_plot", height = 250)),
box(
title = "Controls",
selectInput(inputId = "released_selector",
label = "Year:",
choices = c(2009, 2010, 2011, 2012, 2013, 2014)
)
)
)
)
)
)
# The calculations behind the scenes
server <- function(input, output) {
# Read in the file
patient_data <- read_csv(file = "https://www.ironholds.org/resources/misc/patient_data.csv")
# Generate the admissions plot
output$admissions_plot <- renderPlot({
admissions_data <- patient_data[patient_data$year == input$admissions_selector,]
hist(admissions_data$admitted)
})
# Generate the released plot
output$released_plot <- renderPlot({
admissions_data <- patient_data[patient_data$year == input$released_selector,]
hist(admissions_data$released)
})
}
shinyApp(ui, server)
These are very basic plots, and there are certainly prettier ways of visualising and displaying data in Shiny dashboards. If you’re interested in exploring that side of things, I’d recommend checking out:
- The Harvard University guide to building pretty, customisable plots with ggplot2;
- The dygraph package for interactive visualisations that can be built into Shiny dashboards;
- Further tutorials on Shiny and various visualisation library integrations with it on the RStudio Shiny index
5.4 Optimising dashboards
Before we move on to experimenting with tying all of this material on reading, exploration, cleaning and dashboarding together, there are a couple of ways to optimise shiny dashboards which are worth talking about if you intend to use them on more than your laptop.
The first is about speed; we want dashboards to load quickly for the people using them. One thing about Shiny is that every time a new user opens the dashboard up in their browser, the server
function is re-run to make sure that the user is presented with a fresh, squeaky-clean version of the dashboard.
The problem is that we’re reading data in there. And over the internet, at that! It makes for a noticeable pause in the dashboard showing up, one that will get worse if we end up relying on multiple files or even database queries.
A partial solution is to move the call to read_csv
out of server
, so we end up with:
# Read in the file
patient_data <- read_csv(file = "https://www.ironholds.org/resources/misc/patient_data.csv")
server <- function(input, output) {
# Generate the admissions plot
output$admissions_plot <- renderPlot({
admissions_data <- patient_data[patient_data$year == input$admissions_selector,]
hist(admissions_data$admitted)
})
# Generate the released plot
output$released_plot <- renderPlot({
admissions_data <- patient_data[patient_data$year == input$released_selector,]
hist(admissions_data$released)
})
}
This solves the problem, but moves us too far in the other direction: now the data is only read in once when the dashboard is spun up. What if your data is changing, because you’re relying on (say) daily-updated data?
The solution is to have server
check to see if the data needs updating, and update it if so, but leave it otherwise. That way it will (once a day, in this example) be slow, for one user, once, but be pretty speedy for everyone else and let us keep the dataset up to date. One way of achieving this is:
- When we spin up, note the date and read in the file.
- Every time
server
is run, have it check the current date against the stored date. - If the current date is greater, replace the stored date (and stored file) with new copies.
This looks like:
# Read in the file, and store the date
patient_data <- read_csv(file = "https://www.ironholds.org/resources/misc/patient_data.csv")
stored_date <- Sys.Date()
server <- function(input, output) {
# Check the stored date, update the date and file if we need to
if(Sys.Date() > stored_date){
patient_data <<- read_csv(file = "https://www.ironholds.org/resources/misc/patient_data.csv")
stored_date <<- Sys.Date()
}
# Generate the admissions plot
output$admissions_plot <- renderPlot({
admissions_data <- patient_data[patient_data$year == input$admissions_selector,]
hist(admissions_data$admitted)
})
# Generate the released plot
output$released_plot <- renderPlot({
admissions_data <- patient_data[patient_data$year == input$released_selector,]
hist(admissions_data$released)
})
}
<<-
is a new operator in this course; what it does is tells R that instead of assigning the result of a calculation inside the ‘scope’ of whatever function the calculation was made in, it should assign the result to the ‘global’ scope - ensuring that the assigned object is not deleted when the function is done, like objects inside functions usually are, and that as a consequence we can refer back to them.
Our second bit of optimisation is optimising for changing data. This dataset only has data from 2009 to 2014, but at some point that’s going to change, and if we leave hard-coded selectInput
choices (c(2009, 2010, ...)
) lying around nobody will be able to access the new information.
Instead, let’s populate the choices based on the data itself. Shifting the file-reading to sit above ui
, just for consistency’s sake, we can change choices = c(2009, 2010, 2011, 2012, 2013, 2014)
to choices = unique(patient_data$year)
. That way it will automatically adjust to the choices that incoming data actually contains.
That’s it for now. If you’re reading this and part of the course this material was written for, we’re going to tie everything together using an internal dataset shortly. If you’re not, have fun exploring the many other things R (and Shiny!) can do - and feel free to email me if you have questions, problems or thoughts.


The advantages of decision trees include :
The disadvantages of decision trees include :
We want to train a car to decide weither or not it can drive faster or if it should slow down depending on the terrain. Two features will be taken into account in this project :
I will describe the procedure I went through step by step using decision trees as classifiers.
We first need to create a dataset of terrain with the features bumpiness and steepness along with a label "fast" or "slow". From this labeled dataset, we will be able to build a decision tree to help the car make it's decision : "Should I go slow or fast?"
### Modified from: Udacity - Intro to Machine Learning
import random
def makeTerrainData(n_points):
random.seed(42)
### generate random data for both features 'grade' and 'bumpy' with an error
grade = [random.random() for ii in range(0,n_points)]
bumpy = [random.random() for ii in range(0,n_points)]
error = [random.random() for ii in range(0,n_points)]
### data are labeled depending on their features and error.
### label "slow" if labels = 1.0
### label "fast" if labels = 0.0
labels = [round(grade[ii]*bumpy[ii]+0.3+0.1*error[ii]) for ii in range(0,n_points)]
### adjust labels for extreme cases (>0.8) of bumpiness or steepness
for ii in range(0, len(y)):
if grade[ii]>0.8 or bumpy[ii]>0.8:
labels[ii] = 1.0
### split into train set (75% of data generated) and test sets (25% of data generated)
features = [[gg, ss] for gg, ss in zip(grade, bumpy)]
split = int(0.75*n_points)
features_train = features[0:split]
features_test = features[split:]
labels_train = labels[0:split]
labels_test = labels[split:]
return features_train, labels_train, features_test, labels_test
The outputs are as follows for n_points = 10 :
| features_train | labels_train | features_test | labels_test | ||
|---|---|---|---|---|---|
| grade | bumpiness | slow = 1.0 | fast = 0.0 | grade | bumpiness | slow = 1.0 | fast = 0.0 |
| 0.64 | 0.22 | 1.0 | 0.09 | 0.59 | 0.0 |
| 0.03 | 0.51 | 0.0 | 0.42 | 0.81 | 1.0 |
| 0.28 | 0.03 | 0.0 | 0.03 | 0.01 | 0.0 |
| 0.22 | 0.20 | 0.0 | |||
| 0.74 | 0.64 | 1.0 | |||
| 0.68 | 0.54 | 1.0 | |||
| 0.89 | 0.22 | 1.0 |
For n_points = 1000, we get the following repartition of test points. We consider the feature 'bumpiness' on the x-axis and 'grade' on the y axis. Each feature in a gradient between 0 and 1. Each point previously generated has two coordinates bumpiness and grade. When we plot the test points (features_test) - representing 25% of our generated data - we can see the pattern separating the points labeled 'slow' and 'fast'.
Testing set plotted with their labels
Testing set includes all features_test (grade, bumpiness) with their labels_test (slow or fast)
Now with our training set (features_train), we can train our classifier to predict a point's label depending on its features. We will use the class sklearn.tree.DecisionTreeClassifier(). It can take several parameters, but we will only focus on the min_samples_split which determines the number of samples ending up in each leaves of the tree.
from prep_terrain_data import makeTerrainData
from sklearn import tree
from sklearn.metrics import accuracy_score
### generate the dataset for 1000 points (see previous code)
features_train, labels_train, features_test, labels_test = makeTerrainData(1000)
### create the classifier. Here min_samples_split is set to 2.
clf = tree.DecisionTreeClassifier(min_samples_split= 2)
### fit the training set
clf.fit(features_train, labels_train)
### now let's make predictions on the test set
prediction = clf.predict(features_test)
### measure of the accuracy score by comparing the prediction with the actual labels of the testing set
accuracy = accuracy_score(labels_test, pred)
Here I plotted the points from the testing set (features_test) with their labels (labels_test). On top is the prediction made by the classifier after fitting on the training set. We can see that the classifier makes a pretty good job at separating the points.
Decision tree with min_samples_split = 2
Decision tree with min_samples_split = 50
| min_samples_split | Training time (sec) | Predict time (sec) | Accuracy |
|---|---|---|---|
| 2 | 0.001 | 0.000 | 0.908 |
| 50 | 0.001 | 0.000 | 0.912 |
Here we can see that the accuracy is higher when min_samples_split = 50 than when min_samples_split=2. A low min_samples_split means more edges to the tree so a more precise classification on the training set but not necessarily more accuracy on the test set because this can lead to overfitting. We need to be aware of the fine tuning of parameters in order to improve prediction.
Enron was one of the largest US companies in 2000. At the end of 2001, it had collapsed into bankruptcy due to widespread corporate fraud, known since as the Enron scandal. A vast amount of confidential information including thousands of emails and financial data was made public after Federal investigation.
In this project, I will apply decision trees to identify authors of emails in the Enron Corpus.
A big first part of the project is the preprocessing of emails which is described in more details here.
Once the emails are preprocessed and separated into a training and a testing set, the class sklearn.tree.DecisionTreeClassifier() can be used.
from sklearn import tree
def dt_email (features_train, features_test, labels_train, labels_test):
clf = tree.DecisionTreeClassifier(min_samples_split=40)
t0 = time()
clf.fit(features_train, labels_train)
print ("decision tree training time :", round(time() - t0, 3), "s")
t0 = time()
pred = clf.predict(features_test)
print ("decision tree predict time :", round(time() - t0, 3), "s")
accuracy = accuracy_score(labels_test, pred)
print ("decision tree accuracy :", accuracy)
def main():
from_sara_file = "from_sara.txt"
from_chris_file = "from_chris.txt"
word_data, from_data = preprocess_email(from_sara_file, from_chris_file)
features_train, features_test, labels_train, labels_test = vectorize(word_data, from_data)
dt_email(features_train, features_test, labels_train, labels_test)
if __name__ == '__main__':
main()
| Classification algorithm | Training time (sec) | Predict time (sec) | Accuracy |
|---|---|---|---|
| Decision tree | 61.709 | 0.064 | 98.805 |


M.S. in System Design and Control Engineering
Ulsan National Institute of Science and Technology(UNIST)
Ulsan, Korea
Title of Thesis: In-Process Laser Welding Monitoring by Fusing the Uncertain Signal Information of Multi-Photodiode Sensors
- Research Interest: Self-resilient process control, Data(signal) analytics, and Machine learning
- Academic Performance Scholarship Recipient for every semester
- Core Course: BCI(Brain-Computer Interaction), Advanced Machine Learning(고급기계학습), Advanced Multivariate Statistics and Data Mining(고등다변량기법과 데이터마이닝), Advanced Engineering System Design(고등공학디자인기법), Signals and Systems(공학 및 시스템디자인 특론 I), Optimization(공학 및 시스템디자인 특론 II), Root Cause Analysis (근본원인분석), Image Processing(영상처리)


When the generated scanner is run, it analyzes its input looking for strings which match any of its patterns. If it finds more than one match, it takes the one matching the most text (the "longest match" principle). If it finds two or more matches of the same length, the rule listed first in the ocamllex input file is chosen (the "first match" principle).
Once the match is determined, the text corresponding to the match (called the token) is made available in the form of a string. The action corresponding to the matched pattern is then executed (a more detailed description of actions follows), and then the remaining input is scanned for another match.
If no match is found, the scanner raises the Failure "lexing: empty token" exception.
Now, let's see the examples which shows how the patterns are applied.
rule token = parse | "ding" { print_endline "Ding" } (* "ding" pattern *) | ['a'-'z']+ as word (* "word" pattern *) { print_endline ("Word: " ^ word) } ...
When "ding" is given as an input, the ding and word pattern can be matched. ding pattern is selected because it comes before word pattern. So if you code like this:
rule token = parse | ['a'-'z']+ as word (* "word" pattern *) { print_endline ("Word: " ^ word) } | "ding" { print_endline "Ding" } (* "ding" pattern *) | ...
ding pattern will be useless.
In the following example, there are three patterns: ding, dong and dingdong.
rule token = parse | "ding" { print_endline "Ding" } (* "ding" pattern *) | "dong" { print_endline "Dong" } (* "dong" pattern *) | "dingdong" { print_endline "Ding-Dong" } (* "dingdong" pattern *) ...
When "dingdong" is given as an input, there are two choices: ding + dong pattern or dingdong pattern. But by the "longest match" principle, dingdong pattern will be selected.
Though the "shortest match" principle is not used so frequently, ocamllex supports it. If you want to select the shortest prefix of the input, use shortest keyword instead of the parse keyword. The "first match" principle holds still with the "shortest match" principle.


User
public class User: CreateableSecureStorable, ReadableSecureStorable, DeleteableSecureStorable, GenericPasswordSecureStorable, Hashable
Represents a FoodMob user.
-
The user’s first name (or given name)
Declaration
Swift
private(set) public var firstName: String
-
The user’s last name (or family name)
Declaration
Swift
private(set) public var lastName: String
-
The user’s email address (used as their unique identifier).
Declaration
Swift
private(set) public var emailAddress: String
-
Stores the user’s likes, dislikes, and restrictions.
Declaration
Swift
public var categories = [FoodCategory: Preference]()
-
Get the user’s full name, localized by region.
Declaration
Swift
public var fullName: String
-
Conformance to Hashable protocol
Declaration
Swift
public var hashValue: Int
-
Initializes a new user, typically when they register.
- Parameters:
- firstName: User’s first name/given name.
- lastName: User’s last name/family name.
- emailAddress: User’s email address
- authToken: Authorization token for use with the API
- saveToKeychain: Whether or not the credentials should be stored in the keychain.
Declaration
Swift
public init(firstName: String, lastName: String, emailAddress: String, authToken: String, saveToKeychain: Bool = true)
Parameters
firstName
User’s first name/given name.
lastName
User’s last name/family name.
emailAddress
User’s email address
authToken
Authorization token for use with the API
saveToKeychain
Whether or not the credentials should be stored in the keychain.
- Parameters:
-
Convenience initializer, used for initializing friends.
This object is NOT eligible for communicating with the server as an authenticated user.
Declaration
Swift
public convenience init(firstName: String, lastName: String, emailAddress: String)
Parameters
firstName
User’s given name/first name.
lastName
User’s last name/family name
emailAddress
User’s email address
-
Initialize a user from their email address.
This intializer requires that the user has attempted to log in at least once before, and that the
eraseUser
method has not been called.Declaration
Swift
public init?(emailAddress: String?)
-
Removes the user from the device’s keychain.
Declaration
Swift
public func eraseUser()
-
Returns a user’s preference for a food category.
Declaration
Swift
public func preferenceForCategory(category: FoodCategory) -> Preference
Parameters
category
The category of interest
Return Value
Their preference level.
-
Undocumented
Declaration
Swift
public class User: CreateableSecureStorable, ReadableSecureStorable, DeleteableSecureStorable, GenericPasswordSecureStorable, Hashable
-
Sets preference for a category.
Declaration
Swift
public func setPreference(preference: Preference, forCategory category: FoodCategory)
Parameters
preference
A preference to set on a category
category
The category that is being manipulated
-
Undocumented
Declaration
Swift
public class User: CreateableSecureStorable, ReadableSecureStorable, DeleteableSecureStorable, GenericPasswordSecureStorable, Hashable
-
Undocumented
Declaration
Swift
public class User: CreateableSecureStorable, ReadableSecureStorable, DeleteableSecureStorable, GenericPasswordSecureStorable, Hashable
-
Undocumented
Declaration
Swift
public class User: CreateableSecureStorable, ReadableSecureStorable, DeleteableSecureStorable, GenericPasswordSecureStorable, Hashable


PHOTOGRAPHY FOR YOUR BUSINESS
Pixel PhotoPro
by Nastassia Kharoshka
Your smart choice for professional photography for your business and personal needs.
PixelPhotoProPA@gmail.com


Gallery
Home
Christmas Picnic 2019
Farewell-Au Revoir Les Aînés
Teacher's Day 2020
School Day 2020
Trip to Anandi Magic World
Penfest
Farewell-Bella Ciao
Holi 2020
Miscellaneous
Top


Welcome to My Web Page
Hello. This is div
Hello. This is para 1
This is my replacement
Click Here to Replace
Click Here to Replace


_ense The chemist had to condense the dense mixture before he could dispense it to his customers.[G|D] The immense expense of obtaining frankincense made it a highly sought-after commodity in ancient times.[G|D] The room was filled with an intense aroma of incense as she put on a pretense of joy.[G|D] The suspense that filled the air was thick and tense as everyone waited for the recompense for their hard work.[G|D]


"I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear.
I will permit it to pass over me and through me. And when it has gone past I will turn the inner eye to see its path. Where the fear has gone there will be nothing. Only I will remain"
Bene Gesserit - Litany Against Fear
The Source of All wisdom


| Food | Type |
|---|---|
| Seafood | Shrimps, Crab, Lobster |
| Meat | Chicken, Pork |
| Cheese | Brie, Gruyere |
| Other | Cream sauces |
| Age | Flavor |
|---|---|
| Less Ripe | Green Plum, Green Apple, Pear |
| Medium | Lemon, Peach, Melon |
| More Ripe | Pineapple, Fig, Banana, Mango |
| Oaked | added Cream or Butter |
| Neighbour | Flavour |
|---|---|
| Pinot Gris | Like under-riped Chardonnay |
| Semillion | Lighter with more lemon |
| Viognier | More Vanilla, Flowers or Perfume |
Chardonnay is the world's most popular wine grape.
The taste of the Chardonnay grape is very neutral and easy to like.
Many of the Chardonnay flavors are derived from terroir and oak-aging.
The flavors varies from noticeable acidity (Cold Climates), to crisply and mineral (Chablis, France) with flavors of green plum, apple and pear, to heavy oak and tropical fruit flavors (the New World).
In cooler climates Chardonnay tends to be under-riped.
In warmer climates the flavors tend to vary from lemon to peach and melon.
In very warm climates Chardonnay tends to be over-riped.
Oaked Chardonnay tends to have softer acidity and more fruit flavors with added butter, cream and hazelnut notes.


CKAN Survey Results
Surveyed with
https://github.com/gjlawran/CKAN_Ecosystem
on 2018-July-08
Versions
Extensions
System
CKAN Versions
Name
Number
Sites Implementing
CKAN Extensions
Name
Number
Sites Implementing
Systems Used
Name
Number
Sites Implementing
Todo:
include the surveyed date
one tab per extension, os, version
selectable date
hyperlinks


Go through the Docker Enterprise Standard tutorial, before continuing here with Docker Enterprise Advanced.
In the first tutorial, the fictional company, OrcaBank, designed an architecture with role-based access control (RBAC) to meet their organization’s security needs. They assigned multiple grants to fine-tune access to resources across collection boundaries on a single platform.
In this tutorial, OrcaBank implements new and more stringent security requirements for production applications:
First, OrcaBank adds staging zone to their deployment model. They will no longer move developed appliciatons directly in to production. Instead, they will deploy apps from their dev cluster to staging for testing, and then to production.
Second, production applications are no longer permitted to share any physical infrastructure with non-production infrastructure. OrcaBank segments the scheduling and access of applications with Node Access Control.
Node Access Control is a feature of Docker EE Advanced and provides secure multi-tenancy with node-based isolation. Nodes can be placed in different collections so that resources can be scheduled and isolated on disparate physical or virtual hardware resources.
OrcaBank still has three application teams, payments
, mobile
, and db
with
varying levels of segmentation between them.
Their RBAC redesign is going to organize their UCP cluster into two top-level collections, staging and production, which are completely separate security zones on separate physical infrastructure.
OrcaBank’s four teams now have different needs in production and staging:
security
should have view-only access to all applications in production (but
not staging).db
should have full access to all database applications and resources in
production (but not staging). See DB Team.mobile
should have full access to their Mobile applications in both
production and staging and limited access to shared db
services. See
Mobile Team.payments
should have full access to their Payments applications in both
production and staging and limited access to shared db
services.OrcaBank has decided to replace their custom Ops
role with the built-in
Full Control
role.
View Only
(default role) allows users to see but not edit all cluster
resources.Full Control
(default role) allows users complete control of all collections
granted to them. They can also create containers without restriction but
cannot see the containers of other users.View & Use Networks + Secrets
(custom role) enables users to view/connect
to networks and view/use secrets used by db
containers, but prevents them
from seeing or impacting the db
applications themselves.In the previous tutorial, OrcaBank created separate collections for each
application team and nested them all under /Shared
.
To meet their new security requirements for production, OrcaBank is redesigning collections in two ways:
The collection architecture now has the following tree representation:
/
├── System
├── Shared
├── prod
│ ├── mobile
│ ├── payments
│ └── db
│ ├── mobile
│ └── payments
|
└── staging
├── mobile
└── payments
OrcaBank must now diversify their grants further to ensure the proper division of access.
The payments
and mobile
application teams will have three grants each–one
for deploying to production, one for deploying to staging, and the same grant to
access shared db
networks and secrets.
The resulting access architecture, designed with Docker EE Advanced, provides physical segmentation between production and staging using node access control.
Applications are scheduled only on UCP worker nodes in the dedicated application
collection. And applications use shared resources across collection boundaries
to access the databases in the /prod/db
collection.
The OrcaBank db
team is responsible for deploying and managing the full
lifecycle of the databases that are in production. They have the full set of
operations against all database resources.
The mobile
team is responsible for deploying their full application stack in
staging. In production they deploy their own applications but use the databases
that are provided by the db
team.


--- layout: default title: Thank you! headline : Contact us for anything. description: Our goal is to be as helpful as possible. ---
{%- include navbar.html -%} {%- assign thnk=site.data.schedule_demo.thanks-%} {%- include thanks.html thanks=thnk -%}


Population:
Region:
Capital:
Where in the world?
Dark Mode
Filter by Region
Africa
Americas
Asia
Europe
Oceania