|
Allegro CL version 10.0 |
This document contains the following sections:
7.1 Some menu notation
7.2 Usual menu behavior
7.3 Pull-down and pop-up menus
7.4 Menu bars
7.5 The menu editor
7.6 Getting rid of a menu bar
7.7 Specifying menu titles and shortcuts
7.8 Access keys
7.9 Working with the menu pane
7.10 More on the menu editor toolbar and some anomalies
7.11 The Menu Item group
7.12 How choosing a menu command causes something to happen
7.13 Some examples of getting menus to do something
7.14 Changing a menu or command (menu-item) programmatically
7.15 Doing something just before a menu is displayed
7.16 Menu Classes
7.17 Creating and modifying menus at run time
7.18 Menu tutorial
7.19 Displaying Pop-up Menus
7.20 Shortcut menu tutorial
7.21 Pop-up menu
This is chapter 7 of the User Guide for the Allegro CL 10.0 Integrated Development Environment (IDE).
The chapters of the IDE User Guide are:
Chapter 1: Introduction to the IDE
Chapter 2: The Allegro CL Development Environment (IDE)
Chapter 3: An example
Chapter 4: Projects
Chapter 5: Components
Chapter 6: Designing a user interface using forms
Chapter 7: Menus (this chapter)
Chapter 8: Events
A menu is a pull-down or pop-up list of commands. Because menus can contain any (reasonable) number of commands and are displayed only by user action, they provide a convenient way to group related commands in a way that they are available at any time to users.
Only top-level windows can have a menu bar. A top-level window is a window that is owned by the screen or another top-level window and is not a child window.
In the following illustration, the window has a menu bar with two menus: File and Edit. Each menu can have zero or more menu-items. For the File menu, there are 6 menu items. The horizontal line is a special type of menu-item called a separator. The menus of a menu bar are of type pull-down-menu because as you click on the title of the menu its menu items are displayed.
Menus contain commands. The object corresponding to a command is a menu-item. However, we use menu-item when describing the programmatic aspects of menu but command when discussing the appearance of menus in an application. There are special kinds of menu-items, such as the separator in the illustration above.
A user can point to a command while the menu is visible. The command pointed to is highlighted and if it names a submenu, the submenu is displayed.
You click a menu command not associated with a submenu to effect it (choose can be used in place of click). When a command is clicked, the menu disappears and the action associated with the command is performed.
In the title of a menu or menu item, you can define an access key. An access key lets your user open the menu or access the menu item using the keyboard. If you wanted to open the File menu in the illustration above, you would press ALT+F. To invoke the Exit menu item, you would press "x". Within a particular menu, you should have each access key be as unique and mnemonic as possible.
Shortcut keys are another way for your users to access your menu commands. A shortcut key is (typically) a combination of a key like Control or Alt and a letter or number. It runs a menu item immediately when pressed. In the illustration above, Control-O is the shortcut key for the Open command on the File menu. You should assign shortcut keys to frequently used commands. Windows reserves some shortcut keys for cross-application commands like Cut, Copy and Paste.
A menu command may be associated with another menu (called a submenu). Pointing to (putting the mouse over) the menu command causes the submenu to be displayed and the user can point to or click on a command from the submenu just as with the main menu. (Submenus allow very many commands to be presented rather conveniently since most commands are not displayed at any one time.)
In this document, we specify a menu and command with the menu title followed by a horizontal bar (|) followed by the command name, all in bold. Thus File | Save is the Save command on the File menu. Commands from submenus are shown with additional |'s, thus View | Manage Windows | Iconize is the Iconize command on the Manage Windows submenu of the View menu. (We have been using this notation in other chapters of this document.)
Clicking or choosing a command causes some action. Menus are rarely used simply to display information (although a command can cause an informative dialog to appear). Information can be conveyed incidentally, however. The fact that a command is unavailable (cannot be pointed to or chosen) can be informative. Some editors make the Save command on the File menu unavailable if there are no changes to save and Paste can be made unavailable when there is nothing on the clipboard.
A user can click on no command and cause the menu to disappear. Typically nothing happens when no item is selected and the state of the program is typically the same as if the menu had never been displayed. (We say typically twice in the last sentence because the application programmer can associate any action displaying the menu or pointing to a command or whatever with any effect. If you are programming a game, you might have a Hint menu and cause the game player to lose points if the Hint menu is looked at, whether or not a command is chosen. Or an application designed to study user interfaces may record how often menus are displayed and items chosen as a way of learning about user behavior, perhaps with an eye to improving the interface.)
A pull-down menu is displayed on a menu bar. Menu bars are displayed only on a top-level window. The menu bar is typically always visible and the user can display a menu any time the mouse is available.
Pop-up menus are displayed by specific user action but have no specific location and no title. Pressing or clicking the right mouse button in various locations in the Allegro CL development environment displays a pop-up menu with context-related items. Another pop-up menu is the symbol completion menu displayed by choosing Search | Complete Symbol. While typing in the Debug window or in an editor window. For example, at a prompt in the Debug window, type
string>
and, leaving the cursor right after the >, choose Search | Complete
Symbol. A pop-up menu appears with items string>
and string>=
(the two symbols whose names begin with string>
).
This chapter is concerned with pull-down menus. For information on pop-up menus, see the online help topics pop-up-shortcut-menu and pop-up-lettered-menu.
To add a menubar to a form, select the form, and then either (1) click on Form | Add Menu Bar or (2) select the menu property in the Inspector, and click on the Extended Editor button:
This displays the menu editor.
To have a pull-down menu on a window, you must:
Do all that and you will have a working menu. Here are some more things that you can do:
Each one of these issues will be dealt with below. The basic tool for placing menu bars on top-level windows and creating pull-down menus is the Menu Editor, which we describe next.
The menu editor looks like this:
You are given a default starter menu bar. You can make changes and accept the menu bar by clicking on the OK button. As you work in the menu editor, you will see the menu bar update on your form. If you click the Cancel button, the editor is closed and the menu property is left unchanged (meaning no menu bar if it was nil before, the previous menubar if there was one before). The starter menu bar has a File menu and an Edit menu containing some Windows-standard commands.
If a form has a menu bar and you decide you do not want one on that form, you can get rid of it by clicking Form | Remove Menu Bar while the form is selected. You can also do it by selecting the contents of the menu property when the form is being inspected and replacing those contents with nil (by typing n i l).
The menubar will also not be displayed if you
nil
.
The menu titles, command names, and shortcuts are displayed in the Menu group in the center of the menu editor. The display is an outline control, with leaves (terminal entries with no subentries) corresponding to commands and nodes (entries with subentries) corresponding to menus or submenus. The title of a menu appears in the menu bar or, if the menu is a submenu, as an item in the parent menu. The title is not a command. Clicking on it displays the menu whose title it is.
A shortcut is a key combination which when pressed has the same effect as choosing the menu command. The shortcuts are shown in the second column. Menus do not have shortcuts, only commands, shown as leaves in the display.
An access key in combination with the Alt key lets you display a menu and choose a command from the keyboard. A tilde (~) is a non-displaying character in a menu title or command name which marks the next character as the one to press when the Alt key is down to display a menu or submenu (in a menu or submenu title) or choose a command. Thus, Alt-F displays the File menu (whose title is ~File). Pressing Alt and keeping it down while pressing F (displays the File menu) and N (chooses the New command) is the same as choosing New from the File menu. The character after the tilde is underlined when the menu is displayed. If no tilde appears, the command cannot be selected using Alt and other keys. Note: because of an apparent Windows bug in some versions of Windows (but not in WIndows 2000), access keys may not work when running a project with a form that has a menubar but no other controls. If that happens, then to see access keys working, add any control (e.g., a button) to the form.
The Menu group is the portion of the menu editor labeled Menu Name:
You cannot type in the Menu group. (You make changes that require typing changing a title of a command name, adding or removing a tilde, etc. in the Menu Item pane described below.) When a line is selected (as the ~File line is in the illustration), its details are displayed in the Menu Item group described below and the toolbar buttons will affect it as follows:
If you add an entry (by clicking on the Add button -- with a +), the new entry has the same indentation as the currently selected entry and becomes the selected entry.
If you delete all entries, the menu bar ceases to be visible but still exists. (Make the menu property nil if you want no menubar.)
The Menu Item pane, at the bottom of the Menu Editor, allows you to specify the details of the entries in the Menu group. The details of the selected entry in the Menu Pane are displayed in the Menu Item pane and changes affect that entry only. Only one entry can be selected at a time. Here is the Menu Item pane from the default menus (i.e. the File and Edit menus displayed when the Menu Editor first appears) with the third (~Open) entry selected:
The elements of this pane are:
If you want a tilde displayed, enter two tildes. Thus, if the title is ~~Open, it will display as ~Open and it cannot be chosen using the Alt key.
When you click on a menu command, the system knows what you have done: it knows which window contained the menu bar, it knows which menu contained the command, and it knows which command was chosen. But it doesn't know what to do with this information other than pass it to the application owning the window, so that is what it does. In an Allegro CL application, every menu has an on-click function. When a menu command is clicked, the menu on-click function is called with three arguments: the menu object, the menu-item corresponding to the command clicked on, and the window owning the menubar.
You can specify the menu on-click function in the On Click field of the menu-item group. This field is only active when a menu or submenu (rather than a command) is selected.
The default selection function for the default menus is funcall-menu-item-with-window. This function examines the menu-item, extracts the menu-item value (which in this case is a symbol naming a function of one argument, a window), and funcalls that function with the window (owning the menu bar) as the argument.
It is common to have the menu-item value contain the information about what should be done when the user clicks on a menu command. Menu-item values can be any Lisp object but symbols naming functions are common choices. Since you define the function, you have great flexibility about what it does. The menu on-click function is then just a function which funcalls the menu-item value with whatever arguments you want, often no arguments or just the owning window as an argument.
The menu-item value is specified in the menu-item group when a menu item is selected.
We have already mentioned the predefined function funcall-menu-item-with-window. Another useful predefined selection function is funcall-menu-item, which funcalls the menu-item value with no arguments.
The function pop-up-message-dialog takes arguments stream, title, text, icon, button-label. We are going to construct a menu whose command pops-up a message using that function where the title and text say what command you chose. Now, of course, this is likely not very useful in a real application but if you can get a menu command to pop-up a message like this, you can probably get it to do many other things by emulation.
Our menu will be called Messages and have two commands: Hello and Goodbye. The menu
on-click function will be funcall-menu-item
and the menu-item-values will be the symbols hello-function
and goodbye-function
,
which are defined as follows:
(defun hello-function () (pop-up-message-dialog (screen *system*) "Hello" "Clicked Hello Command!!!" warning-icon "OK"))
(defun goodbye-function () (pop-up-message-dialog (screen *system*) "Goodbye" "Clicked Goodbye Command!!!" warning-icon "OK"))
We define these to the system (by, for example, evaluating them in the debug window). (screen
*system*)
is the screen (meaning the dialog can pop-up anywhere), warning-icon is a
picture of a yellow triangle with an exclamation point, and OK is the label for the
button. Our menu editor after we have defined the new menu (and deleted the default File
and Edit menus) looks like this:
Note that the On Click field when Messages is selected is funcall-menu-item. The On Click field for Messages will only be available after you have added and indented the Hello and Goodbye items, and when it is available, its value starts as funcall-menu-item-with-window. You must edit that to be funcall-menu-item for this example to work.
If we run the form and click on the Hello command in the Messages menu, we see:
If we click on Goodbye, we see:
There is not in this example much information in the menu-items. We could define a selection function that extracts information from the menu-item but processes it itself rather than having the menu-item value do the work. This can be useful when you define menu commands on the fly, based on runtime information. In this case, the menu-item values are strings "Hello" and "Goodbye". The menu-selection function is the following:
(defun my-menu-on-click-function (menu menu-item window) (declare (ignore menu window)) (let ((title (value menu-item)) (message (concatenate 'string "You clicked " (value menu-item) "!!!!"))) (pop-up-message-dialog (screen *system*) title message warning-icon "OK")))
The behavior is the same as before.
Commands are what appear on menus. On our example menus, the commands are Hello and Goodbye. However, the programmatic object associated with a command is a menu-item, which is an instance of class menu-item and has various properties. One is the available property. If mi-var is a variable whose value is a menu item,
(available mi-var)
returns true or false as the menu-item is or is not available. You can use setf with available to change (or set) the availability of a menu-item. Evaluating the following makes the menu-item mi-var unavailable:
(setf (available mi-var) nil)
So, the only issue is how to find a menu-item programatically. There are various ways
to do this, but one of the easiest is to give the object a name and get it using
find-named-object and that name. Here is the menu editor again with our Messages menu. We
have given the Hello menu-item the name :hello-item
(illustrated) and the
Goodbye menu-item the name :goodbye-item
(not shown):
Now let us get a handle on the the form or window. When developing, click on Get Component on the Tools menu and then click on the form (or window, if you are running the form) and then in the debug window, enter
(setq mywin *)
Programmatically, you can use find-window.
Now that mywin is the window with the Messages menu,
(find-named-object :hello-item mywin)
will return the Hello menu-item,
(available (find-named-object :hello-item mywin))
returns true or false as the Hello item is or is not available, and
(setf (available (find-named-object :hello-item mywin)) nil)
makes it unavailable.
The system calls about-to-show-menu with arguments the window and the menu when user action should cause a menu to appear (user action being, e.g., clicking on the menu title on a menu bar or pressing Alt and the underlined letter of the menu title). about-to-show-menu is a generic function. You can write a before method that will run before the system call to the function (making items unavailable, registering the fact that the menu was looked at, or whatever). If you do this, it is best to make your own class of windows and specialize the method on that class. This avoids paying the overhead every time any menu is displayed anywhere within Allegro CL or your application.
There are several classes related to menus. The following is a class diagram showing the menu classes.
window ^ |-- basic-pane | |-- menu ^ |-- windows-menu ^ |---------- menu-bar | |---------- pop-up-menu ^ | |----- pull-down-menu | |----- shortcut-menu | |----- builder-menu
Key: ^ = "is a subclass of", reading up.
Notice that all menus inherit from window. That is a very primitive window class that provides a handle to an OS window object. The menu-item class is not shown because it does not inherit from menu because menu-items are not an OS visible object. Instead, menu-items inherit from standard-object.
There are several different functions related to creating an instance of a menu; the table shows which function should be used for each menu class. menu-item is created with make-instance. A menu-bar is created with make-window. A pop-up-menu, a pull-down-menu, and a shorcut-menu are all created with open-menu.
Code similar to the following is generated if you accept the default menu bar that is created when you open the Menu Editor. It provides the value of the menu keyword argument to make-window. If you want to see the code for yourself, then toggle the menu property on a form without a menu bar to on. In the Menu Editor, click on OK to accept the default menu bar. With the focus on the form, save it using the File | Save command. In the editor, open the .bil file and search for open-menu.
(open-menu (list (make-instance 'menu-item :name :file-menu :title "~File" :value (open-menu (list (make-instance 'menu-item :name 'new-text-editor :title "~New" :value 'new-text-editor :selected nil :available t :event-synonym '(control-key #\N) :help-string "New editor") (make-instance 'menu-item :name 'open-text-file :title "~Open" :value 'open-text-file :selected nil :available t :event-synonym '(control-key #\O) :help-string "Open a file") (make-instance 'menu-item :name :save :title "~Save" :value 'save-text-file :selected nil :available t :event-synonym '(control-key #\S) :help-string "Save to file") (make-instance 'menu-item :name 'save-as-text-file :title "Save ~As..." :value 'save-as-text-file :selected nil :available t :event-synonym nil :help-string "Save to new file") (make-instance 'menu-item :name nil :title "-" :value nil :selected nil :available nil :event-synonym nil :help-string nil) (make-instance 'menu-item :name 'user-close :title "E~xit" :value 'user-close :selected nil :available t :event-synonym '(alt-key vk-f4) :help-string "Exit application")) 'pull-down-menu (screen *system*) :name :file-menu :show-help-strings-as-tooltips nil :on-click 'funcall-menu-item-with-window) :selected nil :available t :event-synonym nil :help-string nil) (make-instance 'menu-item :name :edit-menu :title "~Edit" :value (open-menu (list (make-instance 'menu-item :name 'cut-command :title "~Cut" :value 'cut-command :selected nil :available t :event-synonym '(control-key #\X) :help-string "Copy contents to clipboard and delete") (make-instance 'menu-item :name 'copy-command :title "C~opy" :value 'copy-command :selected nil :available t :event-synonym '(control-key #\C) :help-string "Copy contents to clipboard") (make-instance 'menu-item :name 'paste-command :title "~Paste" :value 'paste-command :selected nil :available t :event-synonym '(control-key #\V) :help-string "Paste contents from clipboard")) 'pull-down-menu (screen *system*) :name :edit-menu :show-help-strings-as-tooltips nil :on-click 'funcall-menu-item-with-window) :selected nil :available t :event-synonym nil :help-string nil)) 'menu-bar (screen *system*) :name :default-menu :show-help-strings-as-tooltips nil :on-click 'funcall-menu-item)
open-menu
(open-menu (list ...) 'pull-down-menu (screen *system*) :on-click 'funcall-menu-item-with-window ...)
Use the open-menu function to create a pop-up-menu or pull-down-menu. The arguments to open-menu are the list of menu-items, the type of menu, the screen, and zero or more keyword arguments for the properties of the menu.
| name | The symbolic name of the menu item |
| title | The title of the command. Place a "~" before the
access key of the command. Use "-" to indicate a menu separator. |
| on-click | The event handler that is invoked when the user clicks on a menu item in the menu |
make-instance
(make-instance 'menu-item :name :new :title "~New" ...)
For every command on a menu, you need to create an instance of menu-item. In general, you need to specify most of the properties of a menu-item to have it perform properly.
| allow-during-modality | A Boolean value indicating whether or not the command can be invoked when a modal dialog is displayed |
| available | A Boolean value indicating whether the command is displayed as available or dimmed |
| event-synonym | The shortcut key(s) that can invoke the command. Values can be nil or a list of key constants/characters. Here are two examples, '(vk-f8) and '(control-key #\N). |
| help-string | A string that is displayed when the user points to the menu item |
| name | The symbolic name of the menu item |
| selected | A Boolean value indicating whether or not the menu item is displayed with a check mark or not. |
| title | The title of the command. Place a "~" before the access key of the command. Use "-" to indicate a menu separator. |
| value | The value of the menu item. If the menu item has sub-items, then the value is a pull-down or pop-up menu. |
(make-instance 'menu-item :name nil :title "-")
Creates a menu separator. When the title of a menu-item is "-", then a menu separator is displayed as a horizontal line in the menu. You can also use the constant, menu-separator, instead of creating a menu-item. Menu items can be placed on more than one menu.
This short tutorial demonstrates creating and customizing an application's menu bar. The sample application is a mini-text editor that extends the default menu bar.
(defclass my-window (text-edit-window) ())
(defclass my-pane (text-edit-pane) ()) (defmethod default-pane-class ((window my-window)) 'my-pane)
(defmethod new-text-editor ((window my-pane)) (setf (file window) nil) (clear-page window))
(defmethod open-text-file ((window my-pane)) (let* ((pathname (ask-user-for-existing-pathname "Edit File" :stream window))) (cond ((or (null pathname) (not (probe-file pathname))) nil) (t (load-file window pathname)))))
(defmethod save-text-file ((window my-pane)) (let ((file-to-save (file (parent window)))) (if (null file-to-save) (setq file-to-save (ask-user-for-new-pathname "Save file as" :allowed-types '(("Text Files" . "*.txt") ("All Files" . "*.*"))))) (if file-to-save (save-file window file-to-save)))) (defmethod save-as-text-file ((window my-pane)) (let ((file-to-save (ask-user-for-new-pathname "Save file as" :allowed-types '(("Text Files" . "*.txt") ("All Files" . "*.*"))))) (if file-to-save (save-file window file-to-save))))
(defmethod about-to-show-menu ((window my-window) menu) (case (name menu) (:edit-menu (multiple-value-bind (start end) (get-selection (frame-child window)) (setf (available (find-named-object 'cut-command menu)) (not (eq start end))) (setf (available (find-named-object 'copy-command menu)) (not (eq start end))) (setf (available (find-named-object 'paste-command menu)) (first (first *clipboard*))))) (t (call-next-method))))
(defmethod background-color-on-click (menu menu-item (window my-window)) (declare (ignore menu)) (setf (background-color (frame-child window)) (value menu-item)))
(defmethod about-to-show-menu ((window my-window) menu) (case (name menu) (:edit-menu (multiple-value-bind (start end) (get-selection (frame-child window)) (setf (available (find-named-object 'cut-command menu)) (not (equalp start end))) (setf (available (find-named-object 'copy-command menu)) (not (equalp start end))) (setf (available (find-named-object 'paste-command menu)) (first (first *clipboard*))))) (:background-menu (let ((color (background-color (frame-child window)))) (dolist (item (menu-items menu)) (setf (selected item) (and (null (string-equal (title item) "-")) (or (and (null color) (null (value item))) (if (and color (value item)) (rgb-equal (symbol-value (value item)) color)))))))) (t (call-next-method))))
Floating menus are implemented using pop-up-menus. A pop-up-menu is similar to a pull-down-menu because it can have one or more menu items. The difference is that a pop-up menu is not activated by clicking on the menu's title or by using the ALT key. Usually, pop-up-menus are opened by using the right mouse click. You cannot use the Menu Editor to specify pop-up-menus.
Pop-up menus that are opened using a mouse right click are called shortcut menus. If you want to define a shortcut menu for your window, you do the following:
There is a more general exported function called pop-up-menu. If you use pop-up-menu, you are responsible for opening the menu, creating the menu items, and positioning the pop-up menu.
The utility function called pop-up-lettered-menu takes a list of strings and displays a pop-up-menu with alphabetized accelerator keys. If the user clicks on an option, then the string of the selected item is returned. Otherwise, nil is returned if no item is selected. pop-up-lettered-menu enables an end user to type a single key to select an item. It is used by the Search | Complete Symbol command in the Integrated Development Environment.
The following steps extend the previous mini-text editor example. For shortcut menus, you cannot use the Menu Editor and will have to work programmatically. We use the menu-example project created in section 7.18 above.
(defclass my-shortcut-menu (shortcut-menu) ())
(defmethod shortcut-menu-class ((window my-pane)) 'my-shortcut-menu)
(defmethod mouse-right-down ((window my-pane) buttons data) (declare (ignore buttons data)) (pop-up-shortcut-menu window))
(defmethod shortcut-commands ((window my-pane) (menu my-shortcut-menu)) (list (make-instance 'menu-item :name :inspect :value 'inspect-command :title "Inspect") menu-separator (make-instance 'menu-item :name :system-background :value 'system-background-color-command :title "System Background" :selected (not (background-color window))) (make-instance 'menu-item :name :blue-background :value 'blue-background-color-command :title "Blue Background" :selected (rgb-equal (background-color window) blue)))) (defmethod inspect-command ((window my-pane)) (inspect window)) (defmethod system-background-color-command ((window my-pane)) (setf (background-color window) nil)) (defmethod blue-background-color-command ((window my-pane)) (setf (background-color window) blue))
Sometimes you may want to use the more general pop-up-menu function rather than pop-up-shortcut-menu. Here we use the menu-example project created in section 7.18 above.
In the menu-example editor, you would need to rewrite the mouse-right-down function to use pop-up-menu instead. Since you are not using the shortcut menu functions, you are responsible for creating the menu and its menu items. The pop-up-menu function displays a pop-up menu at a specified location.
;; Redefined mouse-right-down that calls pop-up-menu. (defmethod mouse-right-down ((window my-pane) buttons data) (declare (ignore buttons data)) (let ((menu (open-menu (list (make-instance 'menu-item :name :inspect :value 'inspect-command :title "Inspect") menu-separator (make-instance 'menu-item :name :system-background :value 'system-background-color-command :title "System Specified Background" :selected (not (background-color window))) (make-instance 'menu-item :name :blue-background :value 'blue-background-color-command :title "Blue Background" :selected (rgb-equal (background-color window) blue))) 'my-shortcut-menu (screen *system*) :window window))) (prog1 (pop-up-menu menu (screen *system*)) (close menu))))
Go to chapter 8. Go to the beginning of this chapter.
Copyright (c) 1998-2015, Franz Inc. Berkeley, CA., USA. All rights reserved.
Documentation for Allegro CL version 10.0. This is the initial (unrevised) page.
Created 2015.1.25.
|
Allegro CL version 10.0 |


We're sorry but etherminer doesn't work properly without JavaScript enabled. Please enable it to continue.
clean


We are going to switch our mindset. From here on out, we'll stop telling the computer how to do its job and instead write a specification of what we'd like as a result. I'm sure you'll find it much less stressful than trying to micromanage everything all the time.
Declarative, as opposed to imperative, means that we will write expressions, as opposed to step by step instructions.
Think of SQL. There is no "first do this, then do that". There is one expression that specifies what'd like from the database. We don't decide how to do the work, it does. When the database is upgraded and the SQL engine optimized, we don't have to change our query. This is because there are many ways to interpret our specification and achieve the same result.
For some folks, myself included, it's hard to grasp the concept of declarative coding at first so let's point out a few examples to get a feel for it.
// imperative
var makes = [];
for (i = 0; i < cars.length; i++) {
makes.push(cars[i].make);
}
// declarative
var makes = cars.map(function(car){ return car.make; });
The imperative loop must first instantiate the array. The interpreter must evaluate this statement before moving on. Then it directly iterates through the list of cars, manually increasing a counter and showing its bits and pieces to us in a vulgar display of explicit iteration.
The map
version is one expression. It does not require any order of evaluation. There is much freedom here for how the map function iterates and how the returned array may be assembled. It specifies what, not how. Thus, it wears the shiny declarative sash.
In addition to being clearer and more concise, the map function may be optimized at will and our precious application code needn't change.
For those of you who are thinking "Yes, but it's much faster to do the imperative loop", I suggest you educate yourself on how the JIT optimizes your code. Here's a terrific video that may shed some light
Here is another example.
// imperative
var authenticate = function(form) {
var user = toUser(form);
return logIn(user);
};
// declarative
var authenticate = compose(logIn, toUser);
Though there's nothing necessarily wrong with the imperative version, there is still an encoded step-by-step evaluation baked in. The compose
expression simply states a fact: Authentication is the composition of toUser
and logIn
. Again, this leaves wiggle room for support code changes and results in our application code being a high level specification.
Because we are not encoding order of evaluation, declarative coding lends itself to parallel computing. This coupled with pure functions is why FP is a good option for the parallel future - we don't really need to do anything special to achieve parallel/concurrent systems.
We will now build an example application in a declarative, composable way. We'll still cheat and use side effects for now, but we'll keep them minimal and separate from our pure codebase.  Let's start by scaffolding the app. Here's the html:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.11/require.min.js"></script>
<script src="flickr.js"></script>
</head>
<body></body>
</html>
And here's the flickr.js skeleton:
requirejs.config({
paths: {
ramda: 'https://cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min',
jquery: 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min'
}
});
require([
'ramda',
'jquery'
],
function (_, $) {
var trace = _.curry(function(tag, x) {
console.log(tag, x);
return x;
});
// app goes here
});
We're pulling in ramda instead of lodash or some other utility library. It includes compose
, curry
, and more. I've used requirejs, which may seem like overkill, but we'll be using it throughout the book and consistency is key. Also, I've started us off with our nice trace
function for easy debugging.
Now that that's out of the way, on to the spec. Our app will do 4 things.
There are 2 impure actions mentioned above. Do you see them? Those bits about getting data from the flickr api and placing it on the screen. Let's define those first so we can quarantine them.
var Impure = {
getJSON: _.curry(function(callback, url) {
$.getJSON(url, callback);
}),
setHtml: _.curry(function(sel, html) {
$(sel).html(html);
})
};
Here we've simply wrapped jQuery's methods to be curried and we've swapped the arguments to a more favorable position. I've namespaced them with Impure
so we know these are dangerous functions. In a future example, we will make these two functions pure.
Next we must construct a url to pass to our Impure.getJSON
function.
var url = function (term) {
return 'http://api.flickr.com/services/feeds/photos_public.gne?tags=' + term + '&format=json&jsoncallback=?';
};
There are fancy and overly complex ways of writing url
pointfree using monoids[^we'll learn about these later] or combinators. We've chosen to stick with a readable version and assemble this string in the normal pointful fashion.
Let's write an app function that makes the call and places the contents on the screen.
var app = _.compose(Impure.getJSON(trace("response")), url);
app("cats");
This calls our url
function, then passes the string to our getJSON
function, which has been partially applied with trace
. Loading the app will show the response from the api call in the console.
We'd like to construct images out of this json. It looks like the srcs are buried in items
then each media
's m
property.
Anyhow, to get at these nested properties we can use a nice universal getter function from ramda called _.prop()
. Here's a homegrown version so you can see what's happening:
var prop = _.curry(function(property, object){
return object[property];
});
It's quite dull actually. We just use []
syntax to access a property on whatever object. Let's use this to get at our srcs.
var mediaUrl = _.compose(_.prop('m'), _.prop('media'));
var srcs = _.compose(_.map(mediaUrl), _.prop('items'));
Once we gather the items
, we must map
over them to extract each media url. This results in a nice array of srcs. Let's hook this up to our app and print them on the screen.
var renderImages = _.compose(Impure.setHtml("body"), srcs);
var app = _.compose(Impure.getJSON(renderImages), url);
All we've done is make a new composition that will call our srcs
and set the body html with them. We've replaced the trace
call with renderImages
now that we have something to render besides raw json. This will crudely display our srcs directly in the body.
Our final step is to turn these srcs into bonafide images. In a bigger application, we'd use a template/dom library like Handlebars or React. For this application though, we only need an img tag so let's stick with jQuery.
var img = function (url) {
return $('<img />', { src: url });
};
jQuery's html()
method will accept an array of tags. We only have to transform our srcs into images and send them along to setHtml
.
var images = _.compose(_.map(img), srcs);
var renderImages = _.compose(Impure.setHtml("body"), images);
var app = _.compose(Impure.getJSON(renderImages), url);
And we're done!
Here is the finished script:
requirejs.config({
paths: {
ramda: 'https://cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min',
jquery: 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min'
}
});
require([
'ramda',
'jquery'
],
function (_, $) {
////////////////////////////////////////////
// Utils
var Impure = {
getJSON: _.curry(function(callback, url) {
$.getJSON(url, callback);
}),
setHtml: _.curry(function(sel, html) {
$(sel).html(html);
})
};
var img = function (url) {
return $('<img />', { src: url });
};
var trace = _.curry(function(tag, x) {
console.log(tag, x);
return x;
});
////////////////////////////////////////////
var url = function (t) {
return 'http://api.flickr.com/services/feeds/photos_public.gne?tags=' + t + '&format=json&jsoncallback=?';
};
var mediaUrl = _.compose(_.prop('m'), _.prop('media'));
var srcs = _.compose(_.map(mediaUrl), _.prop('items'));
var images = _.compose(_.map(img), srcs);
var renderImages = _.compose(Impure.setHtml("body"), images);
var app = _.compose(Impure.getJSON(renderImages), url);
app("cats");
});
Now look at that. A beautifully declarative specification of what things are, not how they come to be. We now view each line as an equation with properties that hold. We can use these properties to reason about our application and refactor.
There is an optimization available - we map over each item to turn it into a media url, then we map again over those srcs to turn them into img tags. There is a law regarding map and composition:
// map's composition law
var law = compose(map(f), map(g)) == map(compose(f, g));
We can use this property to optimize our code. Let's have a principled refactor.
// original code
var mediaUrl = _.compose(_.prop('m'), _.prop('media'));
var srcs = _.compose(_.map(mediaUrl), _.prop('items'));
var images = _.compose(_.map(img), srcs);
Let's line up our maps. We can inline the call to srcs
in images
thanks to equational reasoning and purity.
var mediaUrl = _.compose(_.prop('m'), _.prop('media'));
var images = _.compose(_.map(img), _.map(mediaUrl), _.prop('items'));
Now that we've lined up our map
's we can apply the composition law.
var mediaUrl = _.compose(_.prop('m'), _.prop('media'));
var images = _.compose(_.map(_.compose(img, mediaUrl)), _.prop('items'));
Now the bugger will only loop once while turning each item into an img. Let's just make it a little more readable by extracting the function out.
var mediaUrl = _.compose(_.prop('m'), _.prop('media'));
var mediaToImg = _.compose(img, mediaUrl);
var images = _.compose(_.map(mediaToImg), _.prop('items'));
We have seen how to put our new skills into use with a small, but real world app. We've used our mathematical framework to reason about and refactor our code. But what about error handling and code branching? How can we make the whole application pure instead of merely namespacing destructive functions? How can we make our app safer and more expressive? These are the questions we will tackle in part 2.


NOTE: For this example to work you have to run it in a web server context. This example renders the Gantt chart in Right-To-Left mode. For this to work, you must also use the special RTL versions of Ext JS. See the HTML head section for details.
Note that the js for the example code is not minified so it is readable. See rtl.js.


2.6 Lab: Linear Regression
2.6.1 Libraries
The library()
function is used to load libraries, or groups of functions and data sets that are not included in the base R
distribution.
Basic functions that perform least squares linear regression and other simple analyses come standard with the base distribution, but more exotic functions require additional libraries.
Here we load the MASS
package, which is a very large collection of data sets and functions.
We also load the ISLR
package, which includes the data sets associated with this book.
If you receive an error message when loading any of these libraries, it likely indicates that the corresponding library has not yet been installed on your system.
Some libraries, such as MASS
, come with R
and do not need to be separately installed on your computer.
However, other packages, such as ISLR
, must be downloaded the ﬁrst time they are used. This can be done directly from within R
.
For example, on a Windows system, select the Install package
option under the Packages
tab.
After you select any mirror site, a list of available packages will appear.
Simply select the package you wish to install and R
will automatically download the package.
Alternatively, this can be done at the R
command line via install.packages("ISLR")
.
This installation only needs to be done the first time you use a package.
However, the library()
function must be called each time you wish to use a given package.
2.6.2 Simple Linear Regression
The MASS
library contains the Boston
data set, which records medv
(median house value) for 506 neighborhoods around Boston.
We will seek to predict medv
using 13 predictors such as rm
(average number of rooms per house), age
(average age of houses), and lstat
(percent of households with low socioeconomic status).
## [1] "crim" "zn" "indus" "chas" "nox" "rm" "age"
## [8] "dis" "rad" "tax" "ptratio" "black" "lstat" "medv"
To find out more about the data set, we can type ?Boston
.
We will start by using the lm()
function to ﬁt a simple linear regression model, with medv
as the response and lstat
as the predictor.
The basic syntax is lm(y∼x,data)
, where y
is the response, x
is the predictor, and data is the data set in which these two variables are kept.
## Error in eval(predvars, data, env): object 'medv' not found
The command causes an error because R
does not know where to find the variables medv
and lstat.
The next line tells R
that the variables are
in Boston.
If we attach Boston
, the first line works fine because R
now recognizes the variables.
If we type lm.fit
, some basic information about the model is output.
For more detailed information, we use summary(lm.fit)
.
This gives us p-values and standard errors for the coefficients, as well as the \(R^2\) statistic and F-statistic for the model.
##
## Call:
## lm(formula = medv ~ lstat)
##
## Coefficients:
## (Intercept) lstat
## 34.55 -0.95
##
## Call:
## lm(formula = medv ~ lstat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -15.17 -3.99 -1.32 2.03 24.50
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 34.5538 0.5626 61.4 <0.0000000000000002 ***
## lstat -0.9500 0.0387 -24.5 <0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 6.22 on 504 degrees of freedom
## Multiple R-squared: 0.544, Adjusted R-squared: 0.543
## F-statistic: 602 on 1 and 504 DF, p-value: <0.0000000000000002
We can use the names()
function in order to find out what other pieces of information are stored in lm.fit
.
Although we can extract these quantities by name—e.g. lm.fit$coefficients
—it is safer to use the extractor functions like coef()
to access them.
## [1] "coefficients" "residuals" "effects" "rank"
## [5] "fitted.values" "assign" "qr" "df.residual"
## [9] "xlevels" "call" "terms" "model"
In order to obtain a confidence interval for the coefficient estimates, we can use the confint()
command.
## 2.5 % 97.5 %
## (Intercept) 33.45 35.659
## lstat -1.03 -0.874
The predict()
function can be used to produce confidence intervals and prediction intervals for the prediction of medv
for a given value of lstat
.
## fit lwr upr
## 1 29.8 17.57 42.0
## 2 25.1 12.83 37.3
## 3 20.3 8.08 32.5
For instance, the \(95\%\) confidence interval associated with a lstat
value of 10 is \((24.47, 25.63)\), and the \(95\%\) prediction interval is \((12.828, 37.28)\).
As expected, the confidence and prediction intervals are centered around the same point (a predicted value of \(25.05\) for medv
when lstat
equals 10), but the latter are substantially wider.
We will now plot medv
and lstat
along with the least squares regression line using the plot()
and abline()
functions.
There is some evidence for non-linearity in the relationship between lstat
and medv
.
We will explore this issue later in this lab.
The abline()
function can be used to draw any line, not just the least squares regression line.
To draw a line with intercept a
and slope b
, we type abline(a,b)
.
Below we experiment with some additional settings for plotting lines and points.
The lwd=3
command causes the width of the regression line to be increased by a factor of 3; this works for the plot()
and lines()
functions also.
We can also use the pch
option to create different
plotting symbols.
abline(lm.fit, lwd=3)
abline(lm.fit, lwd=3, col="red")
plot(lstat, medv, col="red")
plot(lstat, medv, pch = 20)
plot(lstat, medv, pch = "+")
plot(1:20, 1:20, pch = 1:20)
Next we examine some diagnostic plots, several of which were discussed in Section 3.3.3.
Four diagnostic plots are automatically produced by applying the plot()
function directly to the output from lm()
.
In general, this command will produce one plot at a time, and hitting Enter will generate the next plot.
However, it is often convenient to view all four plots together.
We can achieve this by using the par()
function, which tells R to split the display screen into separate panels so that multiple plots can be viewed simultaneously.
For example, par(mfrow=c(2,2))
divides the plotting region into a 2 × 2 grid of panels.
Alternatively, we can compute the residuals from a linear regression fit using the residuals()
function.
The function rstudent()
will return the studentized residuals, and we can use this function to plot the residuals against the fitted values.
On the basis of the residual plots, there is some evidence of non-linearity.
Leverage statistics can be computed for any number of predictors using the hatvalues()
function.
## 375
## 375
The which.max()
function identiﬁes the index of the largest element of a vector.
In this case, it tells us which observation has the largest leverage statistic.
2.6.3 Multiple Linear Regression
In order to fit a multiple linear regression model using least squares, we again use the lm()
function.
The syntax lm(y∼x1+x2+x3)
is used to fit a model with three predictors, x1
, x2
, and x3
.
The summary()
function now outputs the regression coefficients for all the predictors.
##
## Call:
## lm(formula = medv ~ lstat + age, data = Boston)
##
## Residuals:
## Min 1Q Median 3Q Max
## -15.98 -3.98 -1.28 1.97 23.16
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 33.2228 0.7308 45.46 <0.0000000000000002 ***
## lstat -1.0321 0.0482 -21.42 <0.0000000000000002 ***
## age 0.0345 0.0122 2.83 0.0049 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 6.17 on 503 degrees of freedom
## Multiple R-squared: 0.551, Adjusted R-squared: 0.549
## F-statistic: 309 on 2 and 503 DF, p-value: <0.0000000000000002
The Boston data set contains 13 variables, and so it would be cumbersome to have to type all of these in order to perform a regression using all of the predictors. Instead, we can use the following short-hand:
##
## Call:
## lm(formula = medv ~ ., data = Boston)
##
## Residuals:
## Min 1Q Median 3Q Max
## -15.594 -2.730 -0.518 1.777 26.199
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 36.459488 5.103459 7.14 0.0000000000033 ***
## crim -0.108011 0.032865 -3.29 0.00109 **
## zn 0.046420 0.013727 3.38 0.00078 ***
## indus 0.020559 0.061496 0.33 0.73829
## chas 2.686734 0.861580 3.12 0.00193 **
## nox -17.766611 3.819744 -4.65 0.0000042456438 ***
## rm 3.809865 0.417925 9.12 < 0.0000000000000002 ***
## age 0.000692 0.013210 0.05 0.95823
## dis -1.475567 0.199455 -7.40 0.0000000000006 ***
## rad 0.306049 0.066346 4.61 0.0000050705290 ***
## tax -0.012335 0.003761 -3.28 0.00111 **
## ptratio -0.952747 0.130827 -7.28 0.0000000000013 ***
## black 0.009312 0.002686 3.47 0.00057 ***
## lstat -0.524758 0.050715 -10.35 < 0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 4.75 on 492 degrees of freedom
## Multiple R-squared: 0.741, Adjusted R-squared: 0.734
## F-statistic: 108 on 13 and 492 DF, p-value: <0.0000000000000002
We can access the individual components of a summary object by name (type ?summary.lm
to see what is available).
Hence summary(lm.fit)$r.sq
gives us the \(R^2\), and summary(lm.fit)$sigma
gives us the RSE.
The vif()
function, part of the car package, can be used to compute variance inflation factors.
Most VIF’s are low to moderate for this data.
The car package is not part of the base R
installation so it must be downloaded the first time
you use it via the install.packages
option in R
.
## crim zn indus chas nox rm age dis rad tax
## 1.79 2.30 3.99 1.07 4.39 1.93 3.10 3.96 7.48 9.01
## ptratio black lstat
## 1.80 1.35 2.94
What if we would like to perform a regression using all of the variables but one?
For example, in the above regression output, age
has a high p-value.
So we may wish to run a regression excluding this predictor.
The following syntax results in a regression using all predictors except age
.
##
## Call:
## lm(formula = medv ~ . - age, data = Boston)
##
## Residuals:
## Min 1Q Median 3Q Max
## -15.605 -2.731 -0.519 1.760 26.224
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 36.43693 5.08012 7.17 0.00000000000272 ***
## crim -0.10801 0.03283 -3.29 0.00107 **
## zn 0.04633 0.01361 3.40 0.00072 ***
## indus 0.02056 0.06143 0.33 0.73799
## chas 2.68903 0.85960 3.13 0.00186 **
## nox -17.71354 3.67931 -4.81 0.00000196711001 ***
## rm 3.81439 0.40848 9.34 < 0.0000000000000002 ***
## dis -1.47861 0.19061 -7.76 0.00000000000005 ***
## rad 0.30579 0.06609 4.63 0.00000475053897 ***
## tax -0.01233 0.00376 -3.28 0.00110 **
## ptratio -0.95221 0.13029 -7.31 0.00000000000110 ***
## black 0.00932 0.00268 3.48 0.00054 ***
## lstat -0.52385 0.04763 -11.00 < 0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 4.74 on 493 degrees of freedom
## Multiple R-squared: 0.741, Adjusted R-squared: 0.734
## F-statistic: 117 on 12 and 493 DF, p-value: <0.0000000000000002
Alternatively, the update()
function can be used.
2.6.4 Interaction Terms
It is easy to include interaction terms in a linear model using the lm()
function.
The syntax lstat:black
tells R
to include an interaction term between lstat
and black
.
The syntax lstat*age
simultaneously includes lstat
, age
, and the interaction term lstat
\(\times\)age
as predictors; it is a shorthand for lstat+age+lstat:age
.
##
## Call:
## lm(formula = medv ~ lstat * age, data = Boston)
##
## Residuals:
## Min 1Q Median 3Q Max
## -15.81 -4.04 -1.33 2.08 27.55
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 36.088536 1.469835 24.55 < 0.0000000000000002 ***
## lstat -1.392117 0.167456 -8.31 0.00000000000000088 ***
## age -0.000721 0.019879 -0.04 0.971
## lstat:age 0.004156 0.001852 2.24 0.025 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 6.15 on 502 degrees of freedom
## Multiple R-squared: 0.556, Adjusted R-squared: 0.553
## F-statistic: 209 on 3 and 502 DF, p-value: <0.0000000000000002
2.6.5 Non-linear Transformations of the Predictors
The lm()
function can also accommodate non-linear transformations of the predictors.
For instance, given a predictor X, we can create a predictor X using I(X^2)
.
The function I()
is needed since the ^
has a special meaning in a formula; wrapping as we do allows the standard usage in R
, which is to raise X
to the power 2
.
We now perform a regression of medv
onto lstat
and \(\color{#B44C1C}{lstat^2}\).
##
## Call:
## lm(formula = medv ~ lstat + I(lstat^2))
##
## Residuals:
## Min 1Q Median 3Q Max
## -15.28 -3.83 -0.53 2.31 25.41
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 42.86201 0.87208 49.1 <0.0000000000000002 ***
## lstat -2.33282 0.12380 -18.8 <0.0000000000000002 ***
## I(lstat^2) 0.04355 0.00375 11.6 <0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 5.52 on 503 degrees of freedom
## Multiple R-squared: 0.641, Adjusted R-squared: 0.639
## F-statistic: 449 on 2 and 503 DF, p-value: <0.0000000000000002
The near-zero p-value associated with the quadratic term suggests that it leads to an improved model.
We use the anova()
function to further quantify the extent to which the quadratic fit is superior to the linear fit.
## Analysis of Variance Table
##
## Model 1: medv ~ lstat
## Model 2: medv ~ lstat + I(lstat^2)
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 504 19472
## 2 503 15347 1 4125 135 <0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Here Model 1 represents the linear submodel containing only one predictor, lstat
, while Model 2 corresponds to the larger quadratic model that has two predictors, lstat
and \(\color{#B44C1C}{lstat^2}\).
The anova()
function performs a hypothesis test comparing the two models.
The null hypothesis is that the two models fit the data equally well, and the alternative hypothesis is that the full model is superior.
Here the F-statistic is 135 and the associated p-value is virtually zero.
This provides very clear evidence that the model containing the predictors lstat
and \(\color{#B44C1C}{\textbf{lstat}^2}\) is far superior to the model that only contains the predictor lstat
.
This is not surprising, since earlier we saw evidence for non-linearity in the relationship between medv
and lstat
.
If we type
then we see that when the \(\color{#B44C1C}{lstat^2}\) term is included in the model, there is little discernible pattern in the residuals.
In order to create a cubic fit, we can include a predictor of the form I(X^3)
.
However, this approach can start to get cumbersome for higher order polynomials.
A better approach involves using the poly()
function to create the polynomial within lm()
. For example, the following command produces a fifth-order polynomial fit:
##
## Call:
## lm(formula = medv ~ poly(lstat, 5))
##
## Residuals:
## Min 1Q Median 3Q Max
## -13.543 -3.104 -0.705 2.084 27.115
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 22.533 0.232 97.20 < 0.0000000000000002 ***
## poly(lstat, 5)1 -152.460 5.215 -29.24 < 0.0000000000000002 ***
## poly(lstat, 5)2 64.227 5.215 12.32 < 0.0000000000000002 ***
## poly(lstat, 5)3 -27.051 5.215 -5.19 0.00000031 ***
## poly(lstat, 5)4 25.452 5.215 4.88 0.00000142 ***
## poly(lstat, 5)5 -19.252 5.215 -3.69 0.00025 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 5.21 on 500 degrees of freedom
## Multiple R-squared: 0.682, Adjusted R-squared: 0.679
## F-statistic: 214 on 5 and 500 DF, p-value: <0.0000000000000002
This suggests that including additional polynomial terms, up to fifth order, leads to an improvement in the model fit! However, further investigation of the data reveals that no polynomial terms beyond fifth order have significant p-values in a regression fit.
Of course, we are in no way restricted to using polynomial transformations of the predictors. Here we try a log transformation.
##
## Call:
## lm(formula = medv ~ log(rm), data = Boston)
##
## Residuals:
## Min 1Q Median 3Q Max
## -19.49 -2.87 -0.10 2.84 39.82
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -76.49 5.03 -15.2 <0.0000000000000002 ***
## log(rm) 54.05 2.74 19.7 <0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 6.91 on 504 degrees of freedom
## Multiple R-squared: 0.436, Adjusted R-squared: 0.435
## F-statistic: 389 on 1 and 504 DF, p-value: <0.0000000000000002
2.6.6 Qualitative Predictors
We will now examine the Carseats
data, which is part of the ISLR
library.
We will attempt to predict Sales
(child car seat sales) in 400 locations based on a number of predictors.
## [1] "Sales" "CompPrice" "Income" "Advertising" "Population"
## [6] "Price" "ShelveLoc" "Age" "Education" "Urban"
## [11] "US"
The Carseats
data includes qualitative predictors such as Shelveloc
, an indicator of the quality of the shelving location—that is, the space within a store in which the car seat is displayed—at each location.
The predictor Shelveloc
takes on three possible values, Bad, Medium, and Good.
Given a qualitative variable such as Shelveloc
, R
generates dummy variables
automatically.
Below we fit a multiple regression model that includes some interaction terms.
##
## Call:
## lm(formula = Sales ~ . + Income:Advertising + Price:Age, data = Carseats)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.921 -0.750 0.018 0.675 3.341
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 6.575565 1.008747 6.52 0.00000000022 ***
## CompPrice 0.092937 0.004118 22.57 < 0.0000000000000002 ***
## Income 0.010894 0.002604 4.18 0.00003566527 ***
## Advertising 0.070246 0.022609 3.11 0.00203 **
## Population 0.000159 0.000368 0.43 0.66533
## Price -0.100806 0.007440 -13.55 < 0.0000000000000002 ***
## ShelveLocGood 4.848676 0.152838 31.72 < 0.0000000000000002 ***
## ShelveLocMedium 1.953262 0.125768 15.53 < 0.0000000000000002 ***
## Age -0.057947 0.015951 -3.63 0.00032 ***
## Education -0.020852 0.019613 -1.06 0.28836
## UrbanYes 0.140160 0.112402 1.25 0.21317
## USYes -0.157557 0.148923 -1.06 0.29073
## Income:Advertising 0.000751 0.000278 2.70 0.00729 **
## Price:Age 0.000107 0.000133 0.80 0.42381
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.01 on 386 degrees of freedom
## Multiple R-squared: 0.876, Adjusted R-squared: 0.872
## F-statistic: 210 on 13 and 386 DF, p-value: <0.0000000000000002
The contrasts()
function returns the coding that R
uses for the dummy variables.
Use ?contrasts
to learn about other contrasts, and how to set them.
R
has created a ShelveLocGood
dummy variable that takes on a value of 1 if the shelving location is good, and 0 otherwise.
It has also created a ShelveLocMedium
dummy variable that equals 1 if the shelving location is
medium, and 0 otherwise.
A bad shelving location corresponds to a zero for each of the two dummy variables.
The fact that the coefficient for ShelveLocGood
in the regression output is positive indicates that a good shelving location is associated with high sales (relative to a bad location).
And ShelveLocMedium
has a smaller positive coefficient, indicating that a medium shelving location leads to higher sales than a bad shelving location but lower sales than a good shelving location.
2.6.7 Writing Functions
As we have seen, R
comes with many useful functions, and still more functions are available by way of R
libraries.
However, we will often be interested in performing an operation for which no function is available.
In this setting, we may want to write our own function.
For instance, below we provide a simple function that reads in the ISLR
and MASS
libraries, called LoadLibraries()
.
Before we have created the function, R
returns an error if we try to call it.
## Error in eval(expr, envir, enclos): object 'LoadLibraries' not found
## Error in LoadLibraries(): could not find function "LoadLibraries"
We now create the function.
Note that the +
symbols are printed by R
and should not be typed in.
The {
symbol informs R
that multiple commands are about to be input.
Hitting Enter after typing {
will cause R
to print the +
symbol.
We can then input as many commands as we wish, hitting Enter after each one.
Finally the }
symbol informs R
that no further commands will be entered.
Now if we type in LoadLibraries
, R
will tell us what is in the function.
## function(){
## library(ISLR)
## library(MASS)
## print("The libraries have been loaded.")
## }
If we call the function, the libraries are loaded in and the print statement is output.
## [1] "The libraries have been loaded."


Thanks for using Recently Closed Tabs
Changelog
version 2.0
- Changelog is only showed if a new major or minor version war released.
- Complete rewrite of the code: no memory leak any more if you don't use the tabShot functionality. With tabShots set to on the extension will crash after some closed tabs (We are currently working on that problem!).
version 1.2.7
- On options page added link to the this infonews page.
- Added links to useful websites on infonews page.
version 1.2.6
- Replaced the divs showing the urls for the popup rct elements with real tooltips.
- Fixed showTabShot bug.
version 1.2.5
- Introduced the Google Analytics.
version 1.2.4
- Fixed date on options page.
version 1.2.3
- Added limit for recently closed tabs to 100. In a future release that you can define this number in the options.
- Added popup to show a detailed date on the options date.
- Removed select box and added range selector for defining the number of shown recently closed tabs element on the popup page.
- Added popup to show the url on the popup page.
version 1.2
- Reimplemented DOM manipulation with JQuery library
- Infos page adjusted for layout purposes.
- Options page adjusted for layout purposes. A lot of infos about the recently closed tabs are placed here.
- Popup page adjusted for layout purposes.
- Added filters to the options page. Now you can add urls to the filters to exclude them from adding to the recently closed tabs list. You also have the possibility to edit the filters.
- Added option to show or not to show the screenshots for the recently closed tabs. Think of that if you switch the screenshots on the extension will consume more memory.
- Added <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> so that my name is written properly ;-)
version <IP_ADDRESS>
- some annoying bugs fixed ;-)
- basic info news page on every version change, should be optimised to get triggered on major and minor version change (ignore the 3rd and 4th part)
- localStorage use and optimised snapshot images (resized via canvas)
- introduced maxTabs parameter to control the length of the popup list
|


--- date: 2012-12-11T00:00:00Z published: true title: Variable Technologies - We are a world of gadgeteers. - Node is so cool. url: /2012/12/11/variable-technologies-we-are-a-world-of-gadgeteers-node-is-so-cool-/ ---


{% extends "layout.html" %} {% block body %}
{{ _('The websetup was not able to finish the pyClanSphere installation successfully.') }}
{% trans %} If you need help for setting up pyClanSphere have a look at the support wepage that will be there once a beta is available :-) {% endtrans %} {% endblock %}


Swiches to settings display
Changes the display to the change settings display where you can modify the properties settings.


Timeseries¶
The Sensor collection manages Sensors for Locations associated with the DataService. Sensor access is restricted to Users or Admins with Permissions for the Sensor and to the Admin who owns the Sensor.
Post Timeseries Datapoints¶
This stores datapoints in the timeseries of the specified Sensorpoint.
The first datapoint that is posted to the uuid defines the datatype for all further timeseries datapoints e.g. if the first datapoint posted to a certain uuid is a int then all further datapoints have to be ints.
-
POST
/api/sensor/timeseries
¶ Within a singe POST request data can be posted to multiple sensor points. The format for each sensor point in the list should be as follows.
JSON Parameters: - sensor_id (string) – UUID of the sensor to which data has to be posted
- samples (list) –
A list of the data points that have to be added to the time-series of the sensor point given by sensor_id. Each item in the list has to be of the following format: {
“time”: A unix timestamp of a sampling,“value”: A sensor value
}
Returns: - success (string) – Returns ‘True’ if data is posted succesfully otherwise ‘False’
Status Codes: - 200 OK – Success
- 401 Unauthorized – Unauthorized Credentials (See HTTP 401)
Example request:
POST /api/sensor/timeseries HTTP/1.1
Accept: application/json; charset=utf-8
[
{
"sensor_id":"a5d6277e-4b51-4056-b9fd-0a6505b4f5a6",
"samples":[
{
"value":24.56,
"time":1225865462
},
{
"value":23.12,
"time":1225865500
}
]
},
{
"sensor_id":"cee06227-72e5-49d2-94f1-20c501ca2afa",
"samples":[
{
"value":24.56,
"time":1225865462
},
{
"value":23.12,
"time":1225865500
}
]
}
]
Example response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"success": "True"
}
Read Timeseries Datapoints¶
This retreives a list of datapoints for the timeseries of the specified Sensorpoint
-
GET
/sensor/<sensor-uuid>/timeseries?start_time=<start_timestamp>&end_time=<end_timestamp>&resolution=<resolution_units>
¶ Parameters: - sensor-uuid (string) – UUID associated with Sensor (compulsory)
- start_time (integer) – The starting point of time from which the timeseries data of this sensor point is desired. Has to be a UNIX timestamp. (compulsory)
- end_time (integer) – The ending point of time till which the timeseries data of this sensor point is desired. Has to be a UNIX timestamp.(compulsory)
- resolution (string) – The resolution of the data required. If not specified will retrieve all the datapoints over the specified interval. Has to be specified in the format time units as an integer + unit identifier e.g. 10s,1m,1h etc. (optional)
Returns: success (string) – Returns ‘True’ if data is retrieved succesfully otherwise ‘False’
- data (struct) – Contains the series
- series (list) – Contains the timeseries data, uuid of the sensor and the column names for the timeseries data
- columns (list) – Contains the names of the columns of the data that is present in the timeseries
- name (string) – uuid of the sensor whose data is being retrieved
- values (list) – Contains the list of timeseries data that has been requested in the order represented by the columns.
Status Codes: - 200 OK – Success
- 401 Unauthorized – Unauthorized Credentials (See HTTP 401)
Note: Both interval and resolution are specified with the time value appended by the type of the value e.g. 10s for 10 seconds or 10m for 10 minutes.
Example request:
GET /sensor/<sensor-uuid>/timeseries?start_time=1445535722&end_time=1445789516&resolution=10s HTTP/1.1
Accept: application/json; charset=utf-8
Example response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"success":"True",
"data": {
"series": [
{
"columns": [
"time",
"inserted_at",
"value"
],
"name": "35b137b2-c7c6-4608-8489-1c3f0ee7e2d5",
"values": [
[
"2015-10-22T17:41:44.762495917Z",
1445535722.0,
22.11
],
[
"2015-10-22T17:43:19.48927063Z",
1445535818.0,
22.23
],
[
"2015-10-22T22:44:53.066248715Z",
1445553913.0,
24.56
]
]
}
]
}
}


[gecode-users] Profiling examples using the Common Lisp Gecode wrapper
Gustavo Gutierrez
ggutierrez at cic.puj.edu.co
Thu Feb 21 16:08:22 CET 2008
On Thu, Feb 21, 2008 at 2:52 AM, Mauricio Toro <mauriciotorob at gmail.com> wrote:
> Hello,
>
> I am doing some profiling to gecode examples under the Common Lisp wrapper.
> For the examples alpha and eq20 it takes around 5 to 10 times more to be
> executed.
> But, when running queens with size = 30 it takes around 100 times more time
> to be executed.
>
> What could be happening?
That could depend on many things. Maybe you are introducing some
overhead by the layer you are developing between CL and gecode, that
is not for free. It may also depends on the CL implementation. My
pinion is to see how much overhead occur when you take the same
problem and change the size, queens is a good example for this. Try
Queens with n=10, n=15, etc. and try to devise some constant relation
between gecode and CL results. That constant relation can give you an
impression f the overhead introduced. If that relation is not constant
maybe there is something wrong with your implementation.
Do not expect to have the same gecode times. Gecode is run as an OS
process and is highly optimized, on the other side you are on the top
of an emulator or virtual machine.
> What other examples should I try?
Try as many examples as you want but try to find a relation instead to
try finding the same results.
As i said before, there can be many things causing that results the
goal then is to quantify how slower is the wrapper compared to
"vanilla" gecode. I would expect to see something like CL = K*G where
CL is the running time of your examples and G is the time spent for
the same problem in gecode. The important thing is K.
Just a hint, be careful you built gecode without debugging support.
Hope this helps,
--
Gustavo Gutierrez
More information about the gecode-users
mailing list


Hi, I am the owner of Craft and Creation Pro. where i do wood work art (pyrography) and painting for varient occasions for my clients. Click on the icon below to get in contact with me.


| Staff ID | Name | Age | |
|---|---|---|---|
| First | Last | ||
| 243342 | John | Smith | 34 |
| 243223 | Beverly | Jones | 40 |
| 384394 | David | Jackson | 33 |
| Staff ID | Name | Age | |
|---|---|---|---|
| First | Last | ||
| 243342 | John | Smith | 34 |
| 243223 | Beverly | Jones | 40 |
| 384394 | David | Jackson | 33 |
| Question | Measures | Beats |
|---|---|---|
| How many beats are in 3 measures? | ||
| How many beats are in 7 measures? |


changes after john's commit
Does this link work?nope,
Erwin here here here here
JSP test link - seems like NO.
Depending on your browser, "open in new tab" might or might not work...
But if you copy/paste the same path http://dev00120:8080/proxytestcase1/proxy.jsp?http://services.arcgisonline.com/ArcGIS/rest/services/?f=pjson then it seems to work.
The only difference I can see is that the one that fail has a referrer.
Changes after john pull request: It also fails in an iframe, probably for same reason:


Partners and catalogs
participants in the digital delta network
Explain what partner is and how to become one.
Search and discover water data in the digital delta network
Online web client to search and discover the digital delta water data network. Currently, there is only one client. Some more text.


Drinking water row: Union Minister Ram Vilas Paswan dares Arvind Kejriwal to accept his challenge
by JKR StaffUnion Minister Ram Vilas Paswan on Monday dared Delhi Chief Minister Arvind Kejriwal to accept his safe drinking water challenge. Speaking in parliament, Paswan, who is the Consumer Affairs Minister, asked Kejriwal to depute a representative from his Aam Aadmi Party in whose presence Delhi’s tap water can be tested by the BIS officials all over again.
This was after a report by Bureau of Indian Standards (BIS) revealed that tap water in Delhi, Chennai and Kolkata had failed on 10 out of 11 parameters. Kejriwal had termed the report fake when Union Minister Harsh Vardhan shared a newspaper report on the topic on Twitter.
Harsh Vardhan had written on Twitter, “Arvind Kejriwal is making Delhiites drink poison in the name of free water. A survey conducted on the drinking water across 20 cities found that the drinking water in Delhi was the most poisonous. The Aam Aadmi Party government, which claims to have achieved so much on development, has failed to even provide drinking water to people.”
Kejriwal had reacted angrily to Harsh Vardhan’s tweet and called it ‘fake news.’  You know that this report is fake and motivated by politics.
Speaking in parliament, Paswan threw a challenge to Kejriwal as he said, “It’s important to supply safe water to everyone. The Centre is ready to form a joint team comprising officials from the Delhi government and the BIS. The water sample from Delhi had failed in the earlier test conducted by the BIS.”
He added, “Delhi Chief Minister Arvind Kejriwal can nominate anyone with a freshwater sample. We can have them tested wherever he (Kejriwal) desires…I nominate Pramod Tiwari, DG BIS, and J Roychowdhury, DDG (Labs) BIS for this purpose.”
Kejriwal is the chairman of the Delhi Jal Board. The national capital goes to polls in February next year. The BIS report on the quality of drinking water in Delhi has the potential to cause immense damage to Kejriwal’s poll prospects. The BJP appears determined to use the BIS report to extract the much-needed electoral mileage.


Short for A Song of Praise Music Festival, ASOP is a songwriting event of the Church dedicated to songs of praise to God. MCGI Leaders launched the songwriting festival as a yearly event in 2004. ASOP turned bi-annual in 2009, giving way to the Mid-Year and Year-End Finals. Though joining or even watching ASOP requires no fee, MCGI leaders have given more than millions of pesos in prizes just to hear the best songs of praise that can be crafted by the hearts and minds of men.
Annually, the Music Ministry holds summits for choirs and theater groups in the Church. Comprised of seminars for craft-improvement, a presentation of song-and-dance numbers, and consultations with Bro. Eli and Bro. Daniel, the summits foster spiritual growth and stability among its members with God's help. of seminars for craft-improvement, a presentation of song-and-dance numbers, and consultations with Bro. Eli and Bro. Daniel, the summits foster spiritual growth and stability among its members with God's help.
To add color to the stage during an event, MCGI's Teatro Kristiano group dances to the beat of heavenly tunes produced by the choir and the ADD Strings, the Church's orchestra. Yearly, they gather at the Convention Center to showcase their dancing and acting skills. Teatro Kristiano groups from different Divisions do their best in interpreting a Church song, aiming to tell the story behind it as visually-pleasing as possible.
While youths participate in the yearly Music and Theater Summits, it is the married couples in the Church who compose the participants of the National Locale Choir Music Summit. Locale Choir representatives from different Church regions give their best in rendering new compositions based on a theme and songs from the hymn book. Locale Choirs are the groups that lead the congregational singing during Church services in the Locales.


A bifurcation diagram is a pretty fractal that demonstrates how very simple functions can give rise to chaotic behaviour.
In this applet we take a function f and iterate it to form a series:
y0 = 0.5Moving horizontally through the image the constant r is slowly varied and for that vertical slice a new series of y values is calculated and plotted.
yn+1 = r * f(yn)
If you pick f(x) = x * (1 - x) then you'll end up with the Logistic Equation version of the bifurcation diagram. This applet starts with this funciton, but by clicking the "Edit Function" button you can edit the plot of f and create your own bifurcation diagram.
The interesting thing about this fractal is that a whole range of functions give rise to chaotic behaviour.
More details: Wikipedia, Paul Bourke.
My other fractals: http://pauldoo.com/fractals/


--- layout: archive title: Zulip Chat Archive permalink: /stream/113489-new-members/topic/is.20this.20a.20use.20case.20for.20quotient.3F.html ---
I want to write a function that takes as input an argument of type list ℤ
and whenever it finds that an integer n
and -n
are both in the list, it removes both of them and replaces them with a 0
(so in some sense the n
and -n
annihilate each other). Repeat process till the list is "completely reduced".
My only thoughts so far have been to define an equivalence relation on list ℤ
and then use that to create a quotient type. Is that right? Is there a simpler way I am missing.
In case it is relevant to the answer, there are two other things I have in mind --
list ℤ
to multiset ℤ
or even set ℤ
. So an answer that would let me switch between these would be preferable (that way I am not tied to some particular property of list
).quot
API. So the easier the final type is to handle, the better.The easiest way I can see to define something like that is
import data.multiset inductive rel : multiset ℤ → multiset ℤ → Prop | mk (n l) : rel (n :: -n :: l) (0 :: l) def quotset : Type := quot rel
This definition doesn't work for list
because it relies on being able to permute stuff. You would have to change the relation to allow permutation
It's also not clear what to do with a set
or finset
because you can accumulate multiple copies of 0
here
Thanks Mario, you really blew my mind with that. :astonished: Till now I was using inductive
only to write nonfunction? types. I had not realized I can define relations using inductive
.
This does not define what "completely reduced" means, there is a proof obligation there quite similar to what we are doing in free groups
{% endraw %}Last updated: Apr 24 2020 at 19:02 UTC


Habitat
Originally from the Kanto region. It can also be found in Joto and Sinnoh. It lives in almost all forests and in other areas where tall grass grows. In such places, you need to be very careful, because the Pokemon can hide behind any leaf and bush. He himself is quite friendly, but if you disturb him, you can get hit with a poisonous thorn. Most Widrills prefer to live in colonies, protected by the beedrill. There, almost nothing threatens them, because few people want to get involved with bidrills.


Boom~~~~~
catch me later.
heng!see you later.


Happy birthday @PragTob (just in time I think with time zones and all). Look forward to meeting in person in Barcelona!
7/29/2014, 2:41:46 PM
Favs: 0
Retweets: 0
link--- layout: tweet ---
Happy birthday @PragTob (just in time I think with time zones and all). Look forward to meeting in person in Barcelona!
7/29/2014, 2:41:46 PM
Favs: 0
Retweets: 0
link


City: Vienna
Age: 28
Education: Ninja University degree
Education: Hobby: sleeping
Education: FB friends: 1000
FB enemies: 0


This is a NodeJs library to help to read/write Shapefile from your disk. API Reference.
ginkgoch-shapefile
package.yarn add ginkgoch-shapefile
In this section, we are going to operate the Shapefile. Before kick off, we need to open the shapefile in case we have everything prepared. We provide three ways to open the shapefile. Choose either one as you use to.
Regular way
const statesShp = new Shapefile('./tests/data/USStates.shp');
statesShp.open();
// put your business logic here.
statesShp.close();
Fluent way to open
const statesShp = new Shapefile('./tests/data/USStates.shp').open();
// put your business logic here.
statesShp.close();
Callback way to open (auto close when callback complete)
const statesShp = new Shapefile('./tests/data/USStates.shp').openWith(() => {
// put your business logic here.
});
In this section, we are going to show you how to iterate shapefile records, get a specific record by id, and how to work with querying filters.
Let's start from a normal case - get record by id.
const statesShp = new Shapefile('./tests/data/USStates.shp').open();
const record = statesShp.get(1); // all ids are started from 1.
| Note: record is a structure formed with geometry
and properties
.
This method fetches all records at once.
const records = statesShp.records();
console.log(records.length);
In previous section, we get all the records at once. It is convenient but it will take much memory usage for sure. Iterator allows to get all features in another way to get records one after another.
const iterator = statesShp.iterator();
let record = undefined;
while ((record = iterator.next()) && !iterator.done) {
console.log(record);
}
We allow to filter the records by following conditions.
{ minx: -180, miny: -90, maxx: 180, maxy: 90 }
.undefined
- Not defined, by default, all fields will be fetched.all
- Same as undefined
.none
- Ignore the dbf querying.string[]
- A specified field name list to fetch. e.g. ["RECID", "NAME"]
.Here is a demo to fetch records from id 10
to 19
, properties include RECID
and STATE_NAME
.
const records = statesShp.records({ from: 10, limit: 10, fields: ['RECID', 'STATE_NAME'] });
Before appending new record, we need to do a little change before opening the Shapefile
. Specify the file flag to 'rs+' or whatever flags to allow the file is able to edit.
const shapefile = new Shapefile(filePath, 'rs+');
shapefile.open();
// put your business logic here.
shapefile.close();
A record
is named as Feature
in Ginkgoch. Let's create a feature first. Then push this feature into shapefile
instance. Then Done.
const feature = new Feature(new Point(0, 0), { NAME: 'Tokyo', POP: 1.268 });
shapefile.push(feature);
Updating a record is similar as appending a new record. The only difference is that, the feature to update requires a valid id.
const feature = new Feature(new Point(0, 0), { NAME: 'Tokyo', POP: 1.268 }, 1 /* the record id to update */);
shapefile.update(feature);
Specify an id to delete.
shapefile.remove(1); // remove the record whose id is 1.
To create a new shapefile, we need to prepare the following factors.
point
, polyLine
, polygon
and multiPoint
.const fields = new Array<DbfField>();
fields.push(new DbfField('RECID', DbfFieldType.number));
fields.push(new DbfField('NAME', DbfFieldType.character, 10));
const shapefile = Shapefile.createEmpty(filePath, ShapefileType.point, fields);
// here the shapefile instance is created with flag 'rs+'. Call open() method to continue appending new records.
Contact<EMAIL_ADDRESS>or submit an issue.
Generated using TypeDoc


Rails 4 was released last June and version 4.0.1 at the beginning of this month. It should be noted that Rails 4 requires Ruby 1.9.3 with Ruby 2.0 being recommended.
Here I highlight my top five features:
Strong Parameters
Rails Mass assignment feature is quite handy but can be really dangerous. Last year Github was compromised by an attack based on it. Rails 4 has a new way of dealing with mass assignment each basically pushes the responsibility out of the model and into the controller where it belongs.
The new approach was already available as a plugin called strong_parameters and dhh as a blog post explaining it in detail.
Live Streaming
Rails 4 brings Lives Streaming as a major new feature. To enable streaming we need to mixin the ruby ActionController::Live
to the controller class. One use case for this is to enable push notifications to the frontend without relying on plugins like Juggernaut or outside services such as Pusher.
You can read more about this feature here and here.
Russian Doll caching
Russian Doll caching describes the technique of using nested fragment caching. The idea is that a page can best divided multiple sections, each one being a fragment. These cached fragments are used independently and can be reused when the content changes. Changes to inner fragments caused a chain reaction which expires all outer fragment caches. Rails 4 uses caches digests to avoid the pain of maintaining cache version numbers for individual templates.
You can read more about Russian Doll caching here
Asynchronous Action Mailer
A new Queue framework was added to Rails 4 and Action Mailer are configured by default to use this new Queue interface. The interface remains very similar to Rails 3 developers with the added benefit that changing backend providers is as easy as changing a single line of code:
1 2 3 4 |
|
You can read more about this feature here
Other smaller enhancements
Rails 4 is now thread-safe by default. You can read about this change on this helpful post by Aaron Patterson.
Another addition is Turbolinks which works similarly to pjax, fetching relevant content and updating the page to avoid a full reload. Read more about it here
There are many more features that I will not be able to cover here. You can read the releases notes to see the detailed changes.
Rails 4.0.1 brings some stability improvements to Rails 4. The complete set of changes are shown here


We're sorry but gdg-web-app doesn't work properly without JavaScript enabled. Please enable it to continue.


Microsoft announce new Fluid Framework Public Preview at Ignite 2019
by SururMicrosoft announced their Fluid Framework at Build 2019 and today at Ignite the company has announced a new Fluid Framework Public Preview for end-users and developers.
Fluid Framework is a new technology and set of experiences that make collaboration seamless by breaking down the barriers between apps. It offers three key capabilities.
- First, experiences powered by the Fluid Framework will support multi-person coauthoring on web and document content at industry-leading speed and scale.
- Second, it provides a componentized document model that allows authors to deconstruct content into collaborative building blocks, use them across applications, and combine them in a new, more flexible kind of document.
- Third, the Fluid Framework makes room for intelligent agents to work alongside humans to translate text, fetch content, suggest edits, perform compliance checks, and more.
Over time, Microsoft expects these capabilities to come to apps across Microsoft 365, including within chat in Teams, mail in Outlook, portals in SharePoint, notes in OneNote, and documents in Office.
For now, the Fluid Framework Public Preview end-user experience will be available publicly, and a private preview for developers, to help them be involved in the development of the technology.


fancyplots
The goal of fancyplots is to make logarithmically scaled x axis scatter plots and nothing more.
You can install the released version of fancyplots from my github repo with:
devtools::install_github("kaitsull/fancyplots")
devtools
!devtools
package, I instantiated the package with the command: create_tidy_package("fancyplots")
and then created a git repo on my local computer via use_git
.use_r("scatter_plot_logx")
, and added documentation using roxygen2
special commenting with the #'
symbol combination.ggplot2
, I added this dependency to my description file using use_package("ggplot2")
use_test("scatterplot")
command (via first using the use_testthat()
function) to write multiple tests which I checked via the check()
function!use_readme_rmd()
and build_readme()
) - as well as a vignette (via use_vignettes()
and build_vingnettes()
)use_github()
BONUS:
scatter_plot_logx
(the only function in this package :sweat_smile:)
Here is how fancyplots
scatter_plot_x function works with the gapminder dataset. In this example, our y axis will be lifeExp, which contains the average life expectancy of each country over the years.
This graph shows population vs life expectancy in the countries of Asia from 1952-2007.
library(fancyplots)
example1 <- filter(gapminder, continent == "Asia")
scatter_plot_logx(example1, "pop", "lifeExp")
#> Removing NA values from the data set...
#> Plotting columns "pop" vs "lifeExp"
#> Printing plot...
Here is how fancyplots
scatter_plot_x function works with the gapminder dataset. In this example, our y axis will be continent.
This graph shows us the spread of the population in the countries of each continent in 2007.
library(fancyplots)
example2 <- filter(gapminder, year == 2007)
scatter_plot_logx(example2, "pop", "continent")
#> Removing NA values from the data set...
#> Plotting columns "pop" vs "continent"
#> Printing plot...
na.omit
The scale_log_x
by default removes any NA’s within your dataset via na.omit = TRUE!
Below is plotting of example 1 with multiple NAs inserted.
#adding NAs to rows 150-250
example1[150:250, ] <- NA
scatter_plot_logx(example1, "pop", "lifeExp", na.omit = TRUE)
#> Removing NA values from the data set...
#> Plotting columns "pop" vs "lifeExp"
#> Printing plot...
#> Warning: Removed 101 rows containing missing values (geom_point).
logx
The scale_log_x
by default scales the x axis of your plot via logx = TRUE!
Below is example 2, plotting with logx = FALSE
scatter_plot_logx(example2, "pop", "continent", logx = FALSE)
#> Removing NA values from the data set...
#> Plotting columns "pop" vs "continent"
#> Printing plot...


Edgar Allan Poe (/poʊ/; born Edgar Poe; January 19, 1809 – October 7, 1849) was an American writer, editor, and literary critic. Poe is best known for his poetry and short stories, particularly his tales of mystery and the macabre. He is widely regarded as a central figure of Romanticism in the United States and of American literature as a whole, and he was one of the country's earliest practitioners of the short story. He is generally considered the inventor of the detective fiction genre and is further credited with contributing to the emerging genre of science fiction. He was the first well-known American writer to earn a living through writing alone, resulting in a financially difficult life and career.


Equivalent to pressing TAB or SHIFT+TAB to move to the next or previous unlocked cell in a protected worksheet. Use these functions when you want to control which cell is active on a protected sheet.
Syntax
UNLOCKED.NEXT( )
UNLOCKED.PREV( )
Related Functions
CELL.PROTECTION Controls protection for the selected cells
PROTECT.DOCUMENT Controls protection for the active sheet
Return to index


@** * Main template for use by all of Emperor's pages. *@ @(title: String, currentProject: Option[Long] = None)(content: Html)(implicit request: AuthenticatedRequest) @import play.api.Play.current @import play.api.Play @import views.html.util._


Welcome to geo_pyspark documentation!¶
Welcome to Documentation. Contents:
Introduction¶
Package is a Python wrapper on scala library GeoSparkSQL. Official repository for GeoSpark can be found at https://github.com/DataSystemsLab/GeoSpark.
Package allow to use all GeoSparkSQL functions and transform it to Python Shapely geometry objects. Also it allows to create Spark DataFrame with GeoSpark UDT from Shapely geometry objects. Spark DataFrame can be converted to GeoPandas easily, in addition all fiona drivers for shape file are available to load data from files and convert them to Spark DataFrame. Please look at examples.
Installation¶
geo_pyspark depnds on Python packages and Scala libraries. To see all dependencies please look at Dependencies section. https://pypi.org/project/pyspark/.
Package needs 3 jar files to work properly:
- geospark-sql_2.2-1.2.0.jar
- geospark-1.2.0.jar
- geo_wrapper.jar
Where 2.2 is a Spark version and 1.2.0 is GeoSpark version. Jar files are placed in geo_pyspark/jars. For newest GeoSpark release jar files are places in subdirectories named as Spark version. Example, jar files for SPARK 2.4 can be found in directory geo_pyspark/jars/2_4.
For older version please find appropriate jar files in directory geo_pyspark/jars/previous.
It is possible to automatically add jar files for newest GeoSpark version. Please use code as follows:
from pyspark.sql import SparkSession
from geo_pyspark.register import upload_jars
from geo_pyspark.register import GeoSparkRegistrator
upload_jars()
spark = SparkSession.builder.\
getOrCreate()
GeoSparkRegistrator.registerAll(spark)
Function
upload_jars()
uses findspark Python package to upload jar files to executor and nodes. To avoid copying all the time, jar files can be put in directory $SPARK_HOME/jars or any other path specified in Spark config files.
Installing from wheel file¶
pipenv run python -m pip install dist/geo_pyspark-0.2.0-py3-none-any.whl
or
pip install dist/geo_pyspark-0.2.0-py3-none-any.whl
Installing from source¶
python3 setup.py install
Examples¶
Integration with GeoPandas and Shapely¶
geo_pyspark has implemented serializers and deserializers which allows to convert GeoSpark Geometry objects into Shapely BaseGeometry objects. Based on that it is possible to load the data with geopandas from file (look at Fiona possible drivers) and create Spark DataFrame based on GeoDataFrame object.
Example, loading the data from shapefile using geopandas read_file method and create Spark DataFrame based on GeoDataFrame:
import os
import geopandas as gpd
from pyspark.sql import SparkSession
from geo_pyspark.data import data_path
from geo_pyspark.register import GeoSparkRegistrator
spark = SparkSession.builder.\
getOrCreate()
GeoSparkRegistrator.registerAll(spark)
gdf = gpd.read_file("gis_osm_pois_free_1.shp")
spark.createDataFrame(
gdf
).show()
+---------+----+-----------+--------------------+--------------------+
| osm_id|code| fclass| name| geometry|
+---------+----+-----------+--------------------+--------------------+
| 26860257|2422| camp_site| de Kroon|POINT (15.3393145...|
| 26860294|2406| chalet| Leśne Ustronie|POINT (14.8709625...|
| 29947493|2402| motel| null|POINT (15.0946636...|
| 29947498|2602| atm| null|POINT (15.0732014...|
| 29947499|2401| hotel| null|POINT (15.0696777...|
| 29947505|2401| hotel| null|POINT (15.0155749...|
+---------+----+-----------+--------------------+--------------------+
Reading data with Spark and converting to GeoPandas
import os
import geopandas as gpd
from pyspark.sql import SparkSession
from geo_pyspark.data import data_path
from geo_pyspark.register import GeoSparkRegistrator
spark = SparkSession.builder.\
getOrCreate()
GeoSparkRegistrator.registerAll(spark)
counties = spark.\
read.\
option("delimiter", "|").\
option("header", "true").\
csv(os.path.join(data_path, "counties.csv"))
counties.createOrReplaceTempView("county")
counties_geom = spark.sql(
"SELECT *, st_geomFromWKT(geom) as geometry from county"
)
df = counties_geom.toPandas()
gdf = gpd.GeoDataFrame(df, geometry="geometry")
gdf.plot()