--- layout: post title: "Grace" description: "Original Short Story" date: 2023-06-05 00:14:45 -0700 background: '/img/032.png' categories: jekyll update ---
Nostalgia kills. It is the greatest love and enemy of humanity, that insatiable yearning for the good old days. I do not regard myself as a particularly nostalgic person, but I have had bursts of flashbacks here and there. That one dark night, as the needle dropped to the soundtrack of my faltering youth, I strained my consciousness from the present and back to California.
For those who had never been to San Francisco, Chinatown is right next to Little Italy. I have always thought that their proximity was deliberate. I never did research on which came first, but I suppose that one must have wanted the other company in this strange, unfamiliar city. Both Chinese and Italian Americans must have been marginalized at one point. It is the common bond they share, the bond of trauma. Settling on the winding hills of San Francisco must have helped the immigrants overcome their loneliness all those years ago.
There is a Chinese proverb that goes, “Two tigers cannot share the same hill.” The Chinese and Italian mafia were at odds in their heyday. I remember watching a film, “The Year of the Dragon” in the cinemas down in Castro. It was 1985. The movie was set in New York City, but I resonated with the location of Little Italy and Chinatown in SF. Overthrowing mafia bosses, drug trading, and all kinds of shady businesses. It is like, loneliness breeds misery, and misery needs company.
My father owned the bakery on the street right between Chinatown and Little Italy back then. Although I had moved away for school at the ripe age of 20, I would go there from time to time to visit my parents. It happened on a cold night of Spring down in San Francisco, as I was about to go home for dinner with my parents. I turned the corner to Stockton with my car. Coit Tower cast its erected shadow upon the dimly lit streets.
It has been a while since I went back home, and a lot has changed around here. A new café across from the bakery has caught my eye. It was Sicilian. I can’t remember the name now, but it was full of island-themed decorations. On the wall of the side hangs oversized posters of Matrimonio all'italiana and Divorzio all'italiana. I do not recall seeing the latter, but I did recall seeing Sophia Loren’s ample bosom in the former. The decoration was alluringly suffocating, it was more Sicilian-tourist themed than Sicilian.
It was strange that a café would open this late at 11 pm, but its warm golden light compelled me to look inside. I have grown accustomed to my solitude, and I didn’t want to see my folks anyway. There I was, walking into this empty cafe without any particular intent. I sat right next to the Matrimonio all'italiana poster, right beneath Loren’s gaze.
I remember the first time I saw her. She passed me from the table swiftly, her shirt almost falling from her lean shoulder. I was too late to turn my head around to see her face, but I felt a slight gust of wind as she walked passed me. I caught a glimpse of her white skirt as it brushed against the side of my shoulder.
I did not know what I wanted going in.  She came out from the kitchen, this time with an apron around her waist. I looked up, and she looks back at me with her hands on the desk. I studied the expression on her face. It was the familiar emotion of tired bleakness from every waitress I had ever seen. She wasn’t particularly beautiful, but there was something unique about her face. She asked if I wanted to order anything. I knew three different Italian dishes, and I definitely do not want pizza or pasta.
“Tiramisu, please.”
She took the menu and left. I suppose that she is the only one working right now, so she also took up the roles of host, waitress, and cashier. I was scanning my surroundings, and there were a lot of intriguing things around me. I can’t remember it clearly now, but I believe that there were a few plants with heads as flower pots. The colorful porcelain eyes stare right back into mine.
Music started playing in the café. I looked up and saw small stereo speakers around the cashier table, with a spinning CD inside, and there it was playing softly, Jeff Buckley’s “Corpus Christi Carol”.
It is a sacred, haunting lullaby. Jeff Buckley’s falsettos were soft yet piercing, and echoing is the soft electric guitar that wanders through this crowded empty chamber.
She came out with the tiramisu. She placed it on the table and left.
The song comes to a close, and I waited in anticipation for the next. If “Eternal Life” is next, that means she is playing the album.
The soft guitar was traded for an aggressive, angry grunge riff. It was indeed, “Eternal Life”.
Buckley was influenced by Led Zepplin, more so than his father’s folk rock. The song forms a stark contrast from “Corpus Christi Carol”, and I adore the placement of the song here.
I wished to tell her how much I love this album. I must first find an excuse.
I barely touched the tiramisu. I was engaged with the music, bopping my head to the harsh rhythm.
“When will I find the strength to bring me release?
Tell me where is the love in what your prophet has said.”
Buckley proposed furiously.
I couldn’t help but sing along. I believe that it might have caught her attention. She shuffled from her kitchen with a cup of espresso.
“You listen to Grace?”
“I do. Jeff Buckley was an amazing artist.”
“I know. It is like his ghost haunts me every time I hear his voice. It is a shame that he only made one album.”
I nodded as she took a sip from her cup.
We took a second to listen to the song.
 “Sometimes I wonder if he hadn’t died so soon, would he make mediocre music and have a vastly different legacy.”
“Like the James Dean of his time.”
“Exactly like the James Dean of his time.”
I turned in the direction of her voice. Her body was leaning against the bar table, with the cup sitting on the edge.
She smiled. “Are you not enjoying the tiramisu?”
I told her I wasn’t actually hungry.
She offered a cup of espresso.
Sure, I replied. I liked where this was going, so I used that as an excuse to continue our conversation.
She came back with her cup and mine. I was caught off-guard by the strong aroma of the bite-sized cup of Italian espresso.
She asked me if I wanted sugar. I declined.
“Y’know, Italians do put sugar in their espressos.”
“I prefer the stripped-down version of things,” I responded, looking at her from above, unaware of the innuendo.
She chuckled lightly.
“Now, why didn’t I expect that from a Jeff Buckley listener?”
“You should’ve.”
She sat across from me at the table. She was leaning towards my side, and I remember thinking that it meant that she wanted to further our conversation. She asked me which song was my favorite from Grace.
“Forget Her” was playing.
I told her, it was this one.
I told her about my story of how this song was almost tailor-made for me when I was 18. Looking back, my teenage heartbreak did not compare to the melancholia evoked in the song.
She swayed left and right to the slow rhythm of the song. She told me it was a nice one, but it wasn’t her favorite.
I finished my cup of espresso.
“Which one is it?”
“I have one in mind.”
“Tell me about it.”
She chuckled, her eyes wandering to the clock. I smiled and pleaded for her to tell me. It wasn’t fair that she knew my favorite song and I don’t know hers.
It was “Lover, You Should’ve Come Over.”
She got up, took my empty cup into hers, and told me to wait for the bill. I couldn’t help but grin at her answer. I thought she was hinting at something. She wasn’t, but I was hoping that she was. It was the song right before “Corpus Christi Carol” on the album.
She placed the tip tray on the table with a receipt. I put a $20 dollar bill on the tip tray, and she took it to the cashier's desk. My thoughts persisted in her direction. “Forget Her” was the last song on the album, so the disc would have been finished at that point.
I read the “Thank you, Please Come Again” from the sign hanging on the door. But I didn’t want this to be over. I got up and walked towards where she was.
I hummed the tune of “Lover, You Should’ve Come Over”:
“Maybe I’m too young
To keep good love from going wrong
But tonight you're on my mind”
She looked up. For a second, a rush of embarrassment and adrenaline came over me.
If anything, she should have walked away and pretended this never happened.
I reached out with my hand over the counter, continuing my serenade in grace.
“Too young to hold on
And too old to just break free and run”
“That’s my favorite lyric,” she responded to my plead with a slightly judgmental smirk.
I withdrew my hand, assuming that it was a rejection.
I walked out of the door, still singing the rest of the tune in my head. I did not dare to look back, wondering if Eurydice was ever going to spare a response. And I did not want to say goodbye, I’d never walk this street again. The street was freezing, I could see the vapor of my breath as I walked across the street.
I unlocked my car parked right next to my father’s store. The thinly frosted metal on the car proved how much time had passed as I spent time there. I looked back to the Sicilian Café, and I see the silhouette of her from the window. She was wearing a long, burgundy coat, with her back facing towards me. She held her hair from her neck with both hands, then adjusted its position to keep the strands on either side of her shoulder. I was still standing on the side of my car, eagerly waiting for a reply.
She dimmed the lights of the Café and walked out of place. Her eyes met mine.
“You’re tearing me apart!” I yelled across the street, giving my best shot of a James Dean impression.
I couldn’t tell if she was rolling her eyes or smiling at me, but she turned her back to me once again and locked the door of the Café. I needed to think of something quick to get her attention. I looked over to either side of the street and walked towards her. I could feel my hands getting colder and colder. Yet, the audacity that I had at that moment overcame the freezing temperature.
“Before you ask, we already had coffee.”
“I was just going to say that I have the Legacy Edition of Grace at home if you’re interested,” I came up with this excuse right before I walked up to her. She was only a few inches shorter than me.
“Right, I’ve yet to hear that one yet.”
“You should’ve expected that from a Jeff Buckley listener,” I replied. We met an hour ago, and we already have our own little inside joke.
I will be honest, I do not recall the intricate details past this point. This was the story of how she and I met, and somehow we ended up at my place. I was too caught up in acting cool, and the truth is, I did not own the extended version of Grace. I saw it in a record store once, but I did not buy the record. I prayed to the Gods that she had never listened to the extended version, so I could carry on pretending that what I owned was, in fact, the extended version. I made sure to put the album on a loop, so this night could last forever.
The night thickens. We spent our time together listening to Grace, from start to finish.
"Kiss me, please kiss me
But kiss me out of desire, babe, not consolation
Oh, you know it makes me so angry
'Cause I know that in time, I'll only make you cry
This is our last goodbye"
Every moment we touched, I was begging my record player to not malfunction so this would last for a while. Every song flew by, and I wished I had the extended version. I was getting to know her, getting to know every inch of her. My mind glitches every time our eyes met, wondering if it was all but a delicate fantasy. It was as if she had awakened something in me, something that had been dormant since I was eighteen.
I have no idea when we winded down, but I remembered the last moments when I looked over at her sleeping, “Hallelujah” was playing. A beautiful rendition of the Leonard Cohen song, Jeff Buckley changed the gospel-styled song into a soft, melancholic ballad. I turned around to observe the subtle rise and fall of her chest as she breathes under the dim light.
“But remember, when I moved in you
And the holy dove was moving too
And every breath we drew was Hallelujah”
That had always been my favorite part of the song. It reminds me of that moment. An eclectic mix of thoughts was at the back of my mind, but all I could focus on was that tranquility with the girl who loves Grace. Her silhouette echoes my lingering gaze. I was studying her ebbs and flows. I thought to myself, there was something asymmetrical about her body. The transition from her waist to her hips was just as peculiar.
A lump came to my throat, and I was ashamed of my voyeuristic impulses. My dimwitted ingenuity had put my mind at unease, and now I must pay for the price. I paused the record and fell asleep on the sofa, wishing that I had never thought of that.
She was gone the next morning. She came and left without a trace, except for the spinning Grace disc in the player. I don’t know her name, but I would like to think it might be Grace. I never liked how it ended, but I assumed that she liked how we ended. I rarely return to my father’s store afterwards, fearing that I would see her again and not knowing what to say.
As I grew older, I realized that it is unwise to find a lover that shares your personal tastes. If you want to separate your individual personality from hers, it is wiser to find someone that you know you are not going to latch on to. I dated women that had vastly different passions and tastes in music from mine. Although I would try to withhold my judgments, I cannot help but feel superior with my objective subjectivity, and not even my wife is immune to my snobbishness. The only woman that I allowed to have the same opinion as me, was the girl that liked Grace.
As for the album, I no longer feel the same adoration for it as I did in the past. Whether it was because of the events of that night, or my own fault for overplaying, I simply do not feel the same about that album over time. It was like an old friend that I haven’t heard from for decades.
That was, until a few days ago, I discover a copy of Sketches for My Sweetheart The Drunk at a vintage record store. My old Grace disc has already been severely scratched, so I thought it was about time to give Jeff Buckley’s posthumous sophomore album a listen. He was only 30 years old when he died of accidental drowning. The album had confirmed my assumptions: it was not as good as Grace, not nearly as good as Grace.
To this day, I still don’t know if she knew I lied about owning the extended version. Perhaps it didn’t matter: although Grace’s extended version has a second disc of studio outtakes from the album, it doesn’t have “Forget Her”. And if I can have anything in common with that, it would be that I haven’t either.
Inspired by:
An Assortment of Short Stories by Haruki Murakami
One line from Taylor Swift’s Cornelia Street
And, of course, Jeff Buckley’s Grace


Date
Speaker
Venue
Date
Speaker
Venue
Details
Topic:
Unavailable
Food:
Unavailable
After:
Unavailable


Getting Started with Hypar
Hypar is a cloud platform for generating buildings that makes it easy to publish, distribute, and maintain your building design logic. Rather than requiring web development skills to scale and deliver your design logic, Hypar lets you concentrate on what you want to get done while the platform creates the interface for your audience and provides computation, visualization, delivery, interoperability, and access control for your functions.
By uploading a function to Hypar you can produce twenty options for a building problem in just a few seconds:
In this introduction to Hypar, we'll work with a much simpler function that generates masses of variable dimensions.
Pre-Flight Check
Before getting started, there are a few things you'll need and some other things you might want to know.
Things you'll need
A 'modern' web browser.
Visual Studio Community (Windows) or VS Code (Windows, Mac OS, Linux)
- We'll use Visual Studio Community or VSC for this guide, but if you're more comfortable in VS Code, take analogous actions in that editor when we come to those steps.
Access to a command line for your operating system.
- We'll use the Windows Command Prompt for this guide, but other operating system command prompts should work similarly.
Microsoft .Net Core 2.1.
- Hypar uses the cross-platform dotnet framework created and maintained by Microsoft. The version number is important! There might be issues with later or earlier versions of .Net Core.
Things you might want to know
- Basic familiarity with the C# programming language will help, but if you're not familiar with C# we'll do our best to walk you through making changes to the initial code you'll get by following the steps in this guide.
- If you want to share your Hypar functions, you have to explicitly make your work public, so don't worry that perfect strangers are judging your work. They will, but only if you decide to make them public.
- The procedures you'll use in this guide compile your code on your desktop and only upload the resulting binary file. No one at Hypar will examine your source code because we won't have it unless you make it public by some other means or if you send it to us so we can help you solve a problem.
- None of the Hypar libraries you'll use in this guide bind your code to Hypar web services. For example, if you'd like to use the Elements library for a desktop application, it's an open source project on GitHub and will always be free for anyone to use or extend. You don't even have to tell us you're using it.
Signing up for Hypar
To upload anything to the Hypar platform, you're going to need an account. Point your browser to https://hypar.io, and you should see a page that looks like this.
If you don't have an account already, click on the Sign Up button below the logo on the right. That should take you to the Sign Up screen.
Fill in a username, an email address you can access, and the password you'd like to use. Then click on the Sign up button in the center of the screen.
Then we do some explaining:
Now check the email account you supplied for a message that looks something like this:
Click on the Verify Email link in the message and you should see a confirmation page:
That's it! You now have a Hypar account. Later in this guide you'll sign in to see your first function in Hypar Explore.
Installing and Using the Hypar Command Line Interface (CLI)
Open a Windows Command Prompt and input the following command:
dotnet tool install -g hypar.cli
Now you're ready to send your first function to Hypar. First use the command prompt cd (for "change directory") command to navigate to the folder where you'd like to place your function project.
Now try this in the same Windows Command Prompt:
hypar new
For consistency with the rest of this example give your function the name StarterFunction. A bunch of stuff happens that we'll explain in a moment, but in the meantime enter your Hypar user name and password:
Now more stuff happens, but the important thing right now is to know that your function has been published (privately) to Hypar!
With one command you've created a web application that we're going to customize and publish again (and again and again), but first let's see what we've got so far. Back to https://hypar.io.
This time, click on the Sign In button on the left below the logo...
...and sign in using your new account:
Once you sign in, you'll see something like the following screen:
All the function cards have default graphics because they haven't produced options yet. Click on the card flipper arrow on the lower right of your StarterFunction card:
That checked Private box is what's keeping your new function invisible to everyone but you and the administrators of Hypar. If you ever want everyone to see your function, you'll have to uncheck that box and read our warning that the world is about to see your work. Try changing the setting, then make it private again.
Now let's make some options. Click on StarterFunction card title. You should see something like this:
Click on the Sample 10 Options in the lower left corner of the page to generate some options.  More importantly, you've just used a web application you created with one command. Let's do a little more exploration into the options before we explain everything that happened. Click on one of the option cards to look at one of them in more detail:
Click in the geometry display window to zoom, pan, and rotate the option.
Now let's go back and look at everything that happened and what it means for writing your own custom Hypar functions in the future. We'll place blue boxes around what we're talking about, like this:
The first thing that happened was that the hypar new command created a new local project folder within the current folder. In Windows Explorer, the new folder looks like this:
The next thing hypar new did was add a test project into that folder, in the folder called test above. This is the project that will help you test your function updates locally before uploading them to Hypar.
Then the hypar new command created a hypar.json file that you can see in the StarterFunction folder above. We'll use this file to customize and extend the default function.
Next the hypar new command executed something called hypar init, which created a couple of more important files in your project, but which you shouldn't have to worry about except to understand what they do and how they change as you edit your hypar.json file.
If you open the src folder, you'll see these files:
The three files with names containing .g.cs are generated code files. You never need to edit these directly but it's important to know that the hypar init command generates them from the hypar.json file that we'll look at in a moment, because when you make changes to the hypar.json file you'll have to run hypar init again to update these files.
These three files are what make your C# code compatible with Hypar services. They're kept separate so that your code won't become directly dependent on the Hypar platform but can easily take advantage of its services.
After hypar init executes, there's some housekeeping that completes the local changes, then after you sign in the hypar new command uploads your new function.
In the StarterFunction folder you'll see a matching StarterFunction.sln file.
Double click on StarterFunction.sln to open it in Visual Studio Community (VSC).
Now use VSC to open the hypar.json file, and we'll look at how this configuration influences what you see when you work with a function on Hypar.
IMPORTANT: Do not use "Add Existing Item" to add the hypar.json to the project. VSC will silently make a copy and place it in the ./src folder, while the Hypar CLI will still read the original file. Just open the file to edit it when you need to.
The "inputs" section of the hypar.json determines what inputs you see for the uploaded function:
The "outputs" section determines the values you see associated with each option:
In the next exercise, we're going add a new Height input to this function.
Adding a new function input
First, let's open the StarterFunctionInputs.g.cs file in VSC and look at the StarterFunctionInputs class to see how the hypar.json turns into code. Note how the corresponding entries become public class properties:
Edit your hypar.json file to look like the one illustrated below. Copy the "Width" output and change the copy's name and description values to refer to "Height" and "The height" instead of "Width" and "The width". VSC politely inserts a comma after the "Width" section's closing brace now that it's no longer the last input field:
Save this file and open the command prompt again, using the cd command to change the current directory to your project folder. Then run hypar init:
hypar init reads the json file to understand how to regenerate the input and output class files as well as the function file. When you return to VSC, if the StarterFunctionInputs.g.cs file is still open, you'll see a warning that the file has been changed externally, which is what hypar init did when it rewrote the three .g.cs files. Select "Yes to All" if you get this warning, and open StarterFunctionInputs.g.cs to see what changed.
Now that we have this new entry for Height, we have to tell our function to use it. Open StarterFunction.cs and change the var height = 1.0; line to use the height input value instead:
Now we have our new Height input. Let's send this up to Hypar using hypar publish and see how our function works now:
Opening StarterFunction on Hypar again, we now have a Height input in addition to the Length and Width inputs. Setting height to 10 gives us 10 options with identical heights and varied lengths and widths:
What if we wanted a maximum height of 20? Let's go back to the hypar.json file and change the maximum for the input range:
Save the file and run hypar publish again at the command prompt:
When we open StarterFunction on Hypar again, the range is changed:
If you click the Sample 10 Options button with the height set to 20, you should see results that look something like this:
Now let's allow Hypar to sample the Height range instead by toggling Select Value to Sample Range on the slider:
Note that Sample 10 Options has become Sample 20 Options. What happened there?
Hypar uses the widest range of values within a group of sliders to determine how many options it will create at once, up to a limit of 20. Since your Height slider now has a min of 1 and a max of 20 with a step of 1, you now can sample 20 options:
Adding a new function output
In Hypar click one of the options and look at the function outputs. You should see something like this, although your volume result might be different:
What if we also wanted to display the area of our cube? For this exercise we'll add a new output to the hypar.json and calculate the result we need. Add these new lines to the hypar.json:
Copy the "Volume" section down and change its "name" field to "Area" and its "description" to "The area". After those changes, on the command line in the project folder run hypar init:
Now in VSC let's see what changed. Open StarterFunctionOutputs.g.cs and you should see your new Area output under the Volume output:
Also note that the StarterFunctionOutputs constructor at the bottom of the illustration has a new double area argument.
That new argument is important when we open StarterFunction.cs in VSC and see that we now have an error condition:
That's because we're still calling StarterFunctionOutputs with just the volume output. We have to calculate and add the area output like this:
Once you've made that change and saved the file, publish the function to Hypar again:
In Hypar return to the StarterFunction options page either by clicking on the browser's back button or by clicking on explore and the StarterFunction card again. Then click on the Sample 20 Options button to arrive at a screen that looks something like this:
Click on one of the options. Now you should see your area calculation on the left side:
What if we wanted to see these results in the graphic display as well? We'll handle that by adding properties to the generated masses, and display them using the Label dropdown. For the moment the Label dropdown only has None as an entry:
To add labels for our volume and area values open StarterFunction.cs in VSC. Move the two value calculations up under the height input, because we're going to need those values a little earlier so we can add them as properties to our mass:
Before we add the mass to our Hypar model, we need to add a couple of NumericProperty entries to the mass. Add these lines before model.AddElement(mass):
There's a lot happening in these two lines, so let's look at each part, using our new Volume property as an example of how both lines work. Since we're adding a property to our mass, we use its method AddProperty:
The first argument this method needs is a name for the new property, which in this case is "Volume":
Next the method needs to know what kind of property you're going to add. Eligible values for this argument are StringProperty (if we were adding a string value), or in this case NumericProperty:
The new NumericProperty needs two arguments: the value, which in this case is the volume variable calculated above, and then the UnitType, which for this output is UnitType.Volume:
Save the file and publish StarterFunction again:
When you return to Hypar, click on the Sample 20 Options button in StarterFunction to create 20 new options, then click on one of the options to open it in the interactive display. In the Label dropdown you should see the two new properties you just added:
Select volume and you should see the value appear in the graphic display on the mass:
Now you see why we specified the UnitType for the new property. Specifying the UnitType as UnitType.Volume tells Hypar to add m3 to the displayed value to denote cubic meters. Internally Hypar maintains all values in metric units. Functions can calculate the conversion to Imperial units if required.
If you select area in the Label dropdown, you should see the value notated in square meters:
You're not limited to simple values like these. You can add any static or calculated value you'd like to any Hypar Element (like Mass).
To learn more about the open source Hypar Elements library browse to the GitHub repository.


Here is a list of upcoming events. Scroll down through the list to see what's happening this month or use the arrows to browse future events and lock in your social calendar. Come join us!
January
Jan. 03 • 7PM
Jan. 04 • 5:30PM
Jan. 05 • 11AM • Tour
Live Music - Rob Anderlik & Mike Church
Jan. 05 • 7:30PM
Jan. 10 • 7PM
Jan. 11 • 3PM
Jan. 12 • 11AM
Jan. 12 • 5:30PM
Jan. 12 • 7:30PM
Jan. 17 • 7PM
Jan. 18 • 5:30PM
Jan. 19 • 5:30PM
Jan. 19 • 7:30PM
Jan. 24 • 7PM
Jan. 25 • 5:30PM
Jan. 26 • 11AM
Jan. 26 • 7:30PM
Jan. 31 • 7PM


public interface IAudioDevice
| Modifier and Type | Field and Description |
|---|---|
static int |
FORMAT_MONO_16
Format for 16 bit mono audio
|
static int |
FORMAT_MONO_8
Format for 8 bit mono audio
|
static int |
FORMAT_STEREO_16
Format for 16 bit stereo audio
|
static int |
FORMAT_STEREO_8
Format for 8 bit stereo audio
|
| Modifier and Type | Method and Description |
|---|---|
int |
createAudioData(byte[] data,
int format,
int sampleRate,
boolean isBigEndian)
Creates a piece of audio data that can be used by this audio device.
|
int |
createAudioObject(int dataId,
double volume,
double pitch,
boolean shouldLoop)
Creates an audio object that completely specifies a playable piece of
sound.
|
void |
dispose()
Releases any resources being used.
|
void |
pause(int objectId)
Stops playing an audio object through this device, but leaves it's
position alone.
|
void |
play(int objectId)
Plays an audio object through this audio device.
|
int |
releaseAudioData(int dataId)
Releases and invalidates a piece of audio.
|
int |
releaseAudioObject(int objectId)
Releases and invalidates an audio object.
|
void |
stop(int objectId)
Stops playing an audio object through this device and resets its position
to the start of the audio.
|
void |
updateAudioObject(int objectId,
double volume,
double pitch,
boolean shouldLoop)
Updates an existing audio object with new parameters.
|
static final int FORMAT_MONO_8
static final int FORMAT_MONO_16
static final int FORMAT_STEREO_8
static final int FORMAT_STEREO_16
void dispose()
int createAudioData(byte[] data, int format, int sampleRate, boolean isBigEndian)
SoundData
class when
possible.data
- The raw array of bytes specifying the piece of audio.format
- The format of data
. Should be one of the
IAudioDevice.FORMAT options.sampleRate
- The number of samples data
specifies per second of
audio.isBigEndian
- True if data
is in big endian byte ordering; false
otherwise.int releaseAudioData(int dataId)
dataId
will be invalid after this call and may be reused to identify a new piece
of audio. Note that this does nothing when the null id, 0, is given for
dataId
.
It is preferred to use this through the SoundData
class when
possible.dataId
- The integer identifying the audio data to be released.int createAudioObject(int dataId, double volume, double pitch, boolean shouldLoop)
Sound
class when
possible.dataId
- The integer that identifies the audio data being played.volume
- How loud the audio should be played at. 1.0 specifies normal
volume, and lower or higher values specify quieter or louder
volumes, respectively.
If the device is incapable of playing audio at the specified
volume, then the audio should be played at the closest
possible volume to the volume specified.pitch
- What pitch the audio should be played at. 1.0 specifies normal
pitch, and lower or higher values specify higher or lower
pitches, respectively.
If the device is incapable of playing audio at the specified
pitch, then the audio should be played at the closest possible
pitch to the pitch specified.shouldLoop
- Whether the audio should automatically restart when finished
playing.void updateAudioObject(int objectId, double volume, double pitch, boolean shouldLoop)
Sound
class when possible.objectId
- The integer that identifies the audio object being updated.volume
- How loud the audio should be played at. 1.0 specifies normal
volume, and lower or higher values specify quieter or louder
volumes, respectively.
If the device is incapable of playing audio at the specified
volume, then the audio should be played at the closest
possible volume to the volume specified.pitch
- What pitch the audio should be played at. 1.0 specifies normal
pitch, and lower or higher values specify higher or lower
pitches, respectively.
If the device is incapable of playing audio at the specified
pitch, then the audio should be played at the closest possible
pitch to the pitch specified.shouldLoop
- Whether the audio should automatically restart when finished
playing.int releaseAudioObject(int objectId)
objectId
will be invalid after this call and may be reused to identify a new audio
object. Note that this does nothing when the null id, 0, is given for
objectId
.
It is preferred to use this through the Sound
class when
possible.objectId
- The integer identifying the audio object to be released.void play(int objectId)
Sound
class when possible.objectId
- The integer identifying the audio object to be played.void stop(int objectId)
Sound
class when possible.objectId
- The integer identifying the audio object to be stopped.void pause(int objectId)
Sound
class
when possible.objectId
- The integer identifying the audio object to be paused.


It provides an extremely efficient procedure for fitting the entire truncated lasso regularization path for linear regression, logistic and multinomial regression models, Poisson regression and the Cox model. The algorithm uses the difference of convex technique. The detail of the algorithm is described in Shen, Pan and Zhu (2012) <doi:10.1080/01621459.2011.645783>. The package is inherited from a popular R package 'glmnet' and many functions in 'glmnet' can be directly used in 'glmtlp'. You can learn more details by the online manual (<http://wuchong.org/glmtlp.html>).
| Version: | 1.1 |
| Depends: | R (≥ 3.1.1), glmnet |
| Published: | 2018-02-07 |
| Author: | Chong Wu and Wei Pan |
| Maintainer: | Chong Wu <wuxx0845 at umn.edu> |
| License: | GPL-2 |
| NeedsCompilation: | no |
| CRAN checks: | glmtlp results |
| Reference manual: | glmtlp.pdf |
| Package source: | glmtlp_1.1.tar.gz |
| Windows binaries: | r-devel: glmtlp_1.1.zip, r-release: glmtlp_1.1.zip, r-oldrel: glmtlp_1.1.zip |
| OS X binaries: | r-release: glmtlp_1.1.tgz, r-oldrel: glmtlp_1.1.tgz |
Please use the canonical form https://CRAN.R-project.org/package=glmtlp to link to this page.


Dash Board
Query Page
Visualizations
Game Page
Search our Game and Requirements - KG
Game Name
Released Year
Minimum Rating
Supported Platforms
Developer
Publisher
Seller
Genre
Select the Genre
{% if genre_list %} {% for i in genre_list %}
{{i}}
{% endfor %} {% endif %}
Theme
Select the Theme
{% if theme_list %} {% for i in theme_list %}
{{i}}
{% endfor %} {% endif %}
Game Mode
Select the Game Mode
{% if game_mode_list %} {% for i in game_mode_list %}
{{i}}
{% endfor %} {% endif %}
Min Price
Max Price
Min Discount
Max Discount
Show all games
Show games supported by my device
Show games not supported by my device
Search


The AEM Best Practices advises you to block access to some developer tools on the AEM author. Access should only be available when accessing the author by IP. The below mapping makes sure that you can access these tools without hitting a block from dispatcher level.
| IP Address (e.g. <IP_ADDRESS>:5433) | AEM Author host (e.g. author-company.host.com) |
|---|


Excalidraw Libraries
A directory of public libraries that you can easily add to Excalidraw.
Follow the instructions if you want to add your own library into this list.
All the following libraries are under MIT License.
Sort By
{label}


SocialSec is a Chrome extension that uses your public social information to help you improve your security.
If you use social media frequently, you may not be aware of how much of your personal information is actually available to the world. This information can be used agianst you in a Social Engineering attack; a type of hacking. SocialSec helps to protect you from these attacks by alerting you whenever a password you create contains some of this public information
After SociaSec is installed, all you need to do is login. Once that is completed, your interaction with Social Sec is complete until it detects a possible security concern. If that happens, SocialSec will send you a notification which will take you to a page where you can address the problem.
We're all human and part of that is not having a perfect memory. Since we need passwords, we tend to create ones that are easier to remember by including names or dates that are familiar. Social Enginnering attacks take advantage of this practice by using known information to make educated guesses about your password rather than just trying everything at once. Social media has made this task much easier for those types of attacks as a great deal of public information is potentially availble for anyone to see.
As SocialSec is still in its infancy, as of right now only permanent public infomation is available to us. In future releases, your wordlist will potentially contain ALL of your public information giving much more control over your security.
SocialSec uses a large set of dates, even ones that were before your birth year, since these dates can still be a part of your public soical media. For example, you may use your mother's birthyear for a password and then post about going to her 43rd birthday.


Meta-documentation
| 1 | Structure |
| 2 | Generating HTML from Pod |
| 3 | Contributing |
| 3.1 | Adding definitions |
This document collection represents the on-going effort to document the Perl 6 programming language with the goals of being: comprehensive; easy to use; easy to navigate; and useful to both newcomers and experienced Perl 6 programmers.
An HTML version of the documentation is located online at https://docs.perl6.org.
The official source for this documentation is located at perl6/doc on GitHub.
This particular document is a quick overview of the process described in more detail in CONTRIBUTING on GitHub. This document also provides a short introduction to writing Perl 6 Pod files, which can be rendered into HTML and other formats.
All of the documentation is written in Perl 6 Pod and kept in the doc/
directory, and the doc/Language/
and doc/Type/
sub-directories. These files are processed as collections of definitions or "documentables", which are then post-processed and linked together.
To generate HTML from the Pod files, you'll need:
A recent version of the Rakudo Perl 6 compiler
The Perl 6 modules Pod::To::HTML, Pod::To::BigPage, and URI::Escape (can be installed via zef).
Optional: GraphViz, for creating graphs of the relationships between Perl 6 types
Optional: Atom Highlights and language-perl6, for syntax highlighting
To generate the documentation into the html/
folder, run:
perl6 htmlify.p6
To host the documentation from a web server, have Perl 5 and Mojolicious::Lite installed, then run:
perl app.pl daemon
The documentation is written in Perl 6 Pod.
For a quick introduction to Perl 6 Pod, see Perl 6 Pod.
For full details about the Perl 6 Pod specification, see Synopsis 26, Documentation.
Documentables can be defined using an =headN
Pod directive, where N
is greater than zero (e.g., =head1
, =head2
, …).
All of the paragraphs and blocks following that directive, up until the next directive of the same level, will be considered part of the documentable. So, in:
=head2 My Definition Some paragraphs, followed by some code: my Code $examples = "amazing"; Mind === blown. =head3 Minor details about My Definition It's fantastic. =head2 And now, for something completely different …
The documentable My Definition extends down to the =head2 And now…
.
Documentables may contain other documentables. Class documentables, for example, often contain the methods the class implements.
Definitions must be in one of the following forms to be recognized as the start of a documentable named, say, þ. First the code in the document source:
=item X<C<How to use the þ infix> | infix,þ> (This a special case, which is always considered a definition) =item C<The þ Infix> =item B<The C<þ> Infix> =item C<Infix þ> =item B<Infix C<þ>> =item C<trait is cached> (A special case for the L<trait|/language/functions#Traits> documentables)
Then the results on the rendered page:
How to use the þ infix
(This is a special case, which is always considered a definition)
The þ Infix
The þ
Infix
Infix þ
Infix þ
trait is cached
(A special case for the trait documentables)
These items should now be searchable by using the search field in the HTML docs.
You can add emphasis with bold ( B<> ) or italicized ( I<> ), with or without code formatting ( C<> ). Due to current parser limitations, special steps have to be taken to use X<> with other formatting codes; for example:
=item X<B<foo>|foo> a fancy subroutine
renders like this
foo a fancy subroutine
Notice that text after a pipe ('|') has no formatting. Also note that C<> preserves spaces and treats text as verbatim.


--- layout: post title: New in 9.3 - DataPort Location ID categories: [] tags: dataport status: publish type: post published: true meta: {} ---
Want to be able to choose which property to use to uniquely identify a Location in DataPort, now you can.


Profile
Awards
Product
Blog
Contact
Marketing Tool
Pendaftaran Mitra
Alamat Toko/Retail
Copyright
Danang Avan Maulana
☰
Want to Talk ?
SEND MESSAGE


Current:
- is the starting point;
- is the ending point;
- are the walls;
- is the open set;
- is the closed set;
- Click and drag from a white cell or from a wall to add and remove walls;
- Click and drag the starting point or the ending point to reposition them;
The Shortest path is: 0 cells long;
It doesn't exists!


Feeling unsafe when going alone? Set the time of your journey before you start.If you don't check in by your scheduled return time, the app will automatically send an email to your emergency contacts. You can stop the timer any time you want.


Welcome to my site about Theater! This site will explain the workings of a true theater.
We will talk about backstage and the front of house. We will see how everything comes together to show off the performance of wonderful artists.
Backstage is where all the magic happens. From the hustle and bustle of all the cast members running around to get to their positions for the next scene to the Stage manager who is always calm and collected to keep the show rolling.
The Stage Manager is the one who controls the backstage area.
They are the one direct connection to the workers in the booth who are controlling lights, sound, etc.
They call out the cues so the booth knows when to cue a certain instance; the start of a new scene, for example. The stage manager is there to make sure everything runs smoothly. A show could not run without a stage manager.
Technicians, AKA Techs, are the miracle workers behind the scenes. Techs are the backbone of backstage.
You see techs buidling props, sewing costumes, and always fixing anything that can go wrong. Techs are there to help get the mood and the set ready so the actors don't have to worry about anything.
They are essential to the theater.
Actors are obviously a huge part of the backstage world. Without them, all of the technicians and crew members would be working so hard for nothing.
Actors are the reason we put on the shows; to show off their amazing talent. Without them, we would all be jobless.
The Front of House is a crucial part to a theater. Not only is it the place where the guests who come to see the show are located during the whole show. With that being said, there are other aspects of the Front of House that are needed to make the experience for the guest a memorable one. This is where all the (techincal) magic happens; Lighting and Sound.
Lighting is pretty important because without it, we wouldn't be able to see the show! So besides that obvious reason, lighting is used to set the mood for a certaion scene.
It really helps the guests folow the story more closely and brings them closer to the show; as if they were there! Lighting can also help the guest divert thier attention at any giving moment.
Sound once again is a huge part of the theater. We all need to be able to hear what is going on the stage! Sound can be pretty tricky because it depends on how the room is built. The acoustics of the room can greatly affect the sound; mostly is bad ways.
So we have to find ways to get the best sound out of a particular room by changing the levels of sound; highs, middles, and lows. To get the sound to sound like they performers are right next to you is the best thing in the world!


"""
flask/components.py:
This sets up the primary data components that allow Flask to connect into the system. This is where the standard
pipeline and other components are created to interact with Flask.
"""
import os
import fprime_gds.common.pipeline.standard
# Module variables, should remain hidden. These are singleton top-level objects used by Flask, and its various
# blueprints needed to run the system.
[docs]def setup_pipelined_components(
debug, logger, config, dictionary, down_store, log_dir, tts_address, tts_port
):
"""
Setup the standard pipeline and related components. This is done once, and then the resulting singletons are
returned so that one object is used throughout the system.
:param logger: logger to use for output
:param config: GDS configuration
:param dictionary: path to F prime dictionary
:param log_dir: log directory to write logs to, and serve logs from
:param tts_address: address to the middleware layer
:param tts_port: port of the middleware layer
:return: F prime pipeline
"""
global __PIPELINE
if (
__PIPELINE is None
and not debug
or os.environ.get("WERKZEUG_RUN_MAIN") == "true"
):
pipeline = fprime_gds.common.pipeline.standard.StandardPipeline()
pipeline.setup(config, dictionary, down_store, logging_prefix=log_dir)
logger.info(
"Connecting to GDS at: {}:{} from pid: {}".format(
tts_address, tts_port, os.getpid()
)
)
pipeline.connect(tts_address, tts_port)
__PIPELINE = pipeline
return __PIPELINE
[docs]def get_pipelined_components():
"""
Returns the setup pipelined components, or raises exception if not setup yet.
:return: F prime pipeline
"""
assert __PIPELINE is not None, "Pipeline must be setup before use"
return __PIPELINE


#include <uvm_ml_adapter.h>
Public Member Functions | |
| uvm_ml_packed_obj () | |
| ~uvm_ml_packed_obj () | |
Static Public Member Functions | |
| static void | allocate (uvm_ml_packed_obj *obj, unsigned nwords) |
Data Fields | |
| unsigned | size |
| unsigned * | val |
| unsigned | max_words |
Definition at line 65 of file uvm_ml_adapter.h.


PDF Check Result
The test carried out by the eAccessibility PDF checker are based on the WCAG 2.0 PDF Techniques.
- Time: 2015-10-24, 17:16
-
Applied Tests:
Total:
0 , Fail: 0 , Pass: 0
The test carried out by the eAccessibility PDF checker are based on the WCAG 2.0 PDF Techniques.


Alert
Use of this service constitutes your agreement for Brāv to use your webcam and microphone.
Ok
Your email must be between 5 and 100 characters long and look like an e-mail address.
Next
Forgot your username?
Your email must be between 5 and 100 characters long and look like an e-mail address.
Please enter a password.
Submit
Forgot your password?
Enter your email
A verification code(OTP) will be sent on your email
Your email must be between 5 and 100 characters long and look like an e-mail address.
Please enter a password.
Send OTP
Back
A verification code(OTP) is sent on your email
Please Verify !!
Verification Code
Please enter OTP.
Verify OTP
Back


|
Square for Android API v0.9 | |||||||
| PREV NEXT | FRAMES NO FRAMES |
Money.currency
.
Square
. While the
current version of Square for Android supports only one line item, this API allows for future
support of multiple line items.image(new Image(url, imageType))
.
price(new Price(amount, currency))
.
squareUp(bill, 0)
.
|
Square for Android API v0.9 | |||||||
| PREV NEXT | FRAMES NO FRAMES |


ARVideoSource
public class ARVideoSource : NSObject, AgoraVideoSourceProtocol
A custom video source for the AgoraRtcEngine. This class conforms to the AgoraVideoSourceProtocol and is used to pass the AR pixel buffer as a video source of the Agora stream.
-
Undocumented
Declaration
Swift
public func captureType() -> AgoraVideoCaptureType
-
Undocumented
Declaration
Swift
public func contentHint() -> AgoraVideoContentHint
-
Undocumented
Declaration
Swift
public var consumer: AgoraVideoFrameConsumer?
-
Undocumented
Declaration
Swift
public var rotation: AgoraVideoRotation
-
Undocumented
Declaration
Swift
public func shouldInitialize() -> Bool
-
Undocumented
Declaration
Swift
public func shouldStart()
-
Undocumented
Declaration
Swift
public func shouldStop()
-
Undocumented
Declaration
Swift
public func shouldDispose()
-
Undocumented
Declaration
Swift
public func bufferType() -> AgoraVideoBufferType


angular
.module('App')
.controller 'IncidentCtrl', ['$scope', 'Incident', 'Config', ($scope, Incident, Config) ->
$scope.incidents = Incident.query()
$scope.cancelIncident = (incident) ->
cancelReason = prompt('Are you sure you want to cancel this incident? Reason:')
console.log('Cancel Reason: ', cancelReason)
if cancelReason? && cancelReason != ''
incident.cancel(cancelReason)
dispatcher = new WebSocketRails(Config.websocket_url)
dispatcher.on_open = (data) ->
console.log('Connection has been established (IncidentCtrl): ', data)
channel = dispatcher.subscribe('incident')
channel.bind('create', (data) ->
console.log('Got new incident from server: ', data)
$scope.$apply ->
$scope.incidents.unshift(new Incident(data))
)
channel.bind('update', (data) ->
console.log('Got updated incident from server: ', data)
for incident in $scope.incidents
if incident.id == data.id
$scope.$apply ->
incident.state = data.state
incident.state_string = data.state_string
incident.help_message = data.help_message
incident.location = data.location
incident.incident_commander = data.incident_commander
incident.created_at_string = data.created_at_string
incident.updated_at_string = data.updated_at_string
)
]


Essays by Spencer Elliott Thompson
My name is Spencer Thompson. I'm the founder of Sokanu and The Prelude Institute. I'm curious about a variety of subjects, including: human development, neuroscience, psychology, cities, technology, politics, inequality and tennis.
Inspired by the London & New York Review of Books - I will attempt to construct each essay as a sythesis of ideas from multiple sources; and add a unique take. A theme that will run throughout the essays below is a strong belief in a sense of moral obligation.
The book reviews:
The essays:


Apple hires National Geographic's former director of podcasts
Source: iMore
What you need to know
- Apple has reportedly hired Emily Ochsenschlager as it ramps up its bid to produce original podcast content.
- Emily Ochsenschlager was previously director of podcasts at National Geographic.
- She also previously worked at NPR for over 15 years as a producer and editor.
A report suggests Apple has hired Emily Ochsenschlager as it sets its sights on producing original podcast content.
A report from Inside via 9to5Mac suggests Apple's latest pickup remains unofficial at this time.
According to the report:
Yesterday, I stumbled across the unofficial news that Apple has hired Emily Ochsenschlager, National Geographic's current director of podcasts and an ex-reporter/producer for NPR. According to a source, the move is "the first of many content hires for them in the coming weeks." This development appears to support a story Bloomberg published last summer, which claimed that Apple "plans to bankroll original podcasts to fend off rivals."
Emily Ochsenschlager worked as director of podcasts at National Geographic for just over a year. Before that, she worked at NPR as a producer and editor for over 15 years. The news would seem to follow a Bloomber report earlier this year, which suggested that Apple planned to fund original podcasts exclusive to its audio service, in order to keep it ahead of rivals such as Spotify. That story also reported that Apple execs had been reaching out to media companies to discuss buying exclusive rights to podcasts, pursuing "the kind of deals it didn't make before."
Up until now, Apple's Podcasts app has served only as a platform for content creators. However, a new Podcasts service (Podcasts+ anyone?) would fall in line with the direction Apple has begun to push its other services, such as TV, news, gaming and music. Hiring a big name like Emily Ochsenschlager could prove Apple's intent to take on original podcasting content in much the same way it has TV. The report notes this could be "the first of many" hires, further suggesting Apple may be planning to pool as many talented people from the industry as possible..
If Apple did indeed start producing podcasts as part of some kind of service, would that interest you? One of the best things about podcasts just now is that they are mostly free, however as streaming services and premium music subscriptions have shown, there is always a market to turn a free, ad-fuelled service into a paid, premium one without encroachment of commercials, perhaps podcasts may turn out to be the same.


TweenJS Example
This example shows how to use TweenJS, including animation, delays, and function calls


{%- extends "relations/base.html" %} {% block content %} A relation has been created for id {{ arxiv_id }} version {{ arxiv_ver }} of type {{ resource_type }} with description {{ description }} {% endblock content %}


and
should include the full main package
name."modelica://..."
 Modelica.Mechanics.MultiBody.UsersGuide.Tutorial.LoopStructures.PlanarLoops
appears as
Modelica.Mechanics.MultiBody.UsersGuide.Tutorial.LoopStructures.PlanarLoops
 The feeder cables are connected to an  induction machine.

appears as
The feeder cables are connected to an induction machine.


Do you want to {{#if emailNotificationsEnabled}}disable{{else}}enable{{/if}} e-mail notifications?
When enabled you will recieve an e-mail notification when new chat session appear.


PSALMS 102
A prayer written by someone who was afflicted, when he was discouraged and pleaded for Yahweh to help.
1 Yahweh, listen to what I am praying;
hear me while I cry out to you!
2 Do not turn away!
Listen to me,
and answer me quickly now when I am calling out to you!
3 My life is ending like smoke that disappears;
I have a big fever which burns my body as a fire burns.
4 I feel as though I am drying up like grass that has been mown,
and I do not even think about eating any food.
5 I groan loudly,
and my bones can be seen under my skin because I have become very thin.
6 I am like a lonely and despised vulture in the desert,
like an owl by itself in the abandoned ruins of a building.
7 I lie awake at night;
because there is no one to comfort me,
I am like a lonely bird sitting on a housetop.
8 All during each day my enemies insult me;
those who make fun of me mention my name
and say, "May you be like him" when they curse people.
9-10 Because you are very angry with me,
I sit in ashes while I am suffering greatly;
those ashes fall on the bread that I eat,
and what I drink has my tears mixed with it.
It is as though you have picked me up and thrown me away!
11 My time to remain alive is short
like an evening shadow that will soon be gone.
I am withering as grass withers in the hot sun.
12 But Yahweh, you are our king who rules forever;
people who are not yet born will remember you.
13 You will arise and act mercifully toward the people of Jerusalem;
it is now time for you to do that;
this is the time for you to be kind to them.
14 Even though the city has been destroyed,
we who serve you still love the stones that were formerly in the city walls;
because now there is rubble everywhere,
we, your people, are very sad when we see it.
15 Yahweh, someday the people of other nations will have an awesome respect for you;
all the kings on the earth will see that you are very glorious.
16 You will rebuild Jerusalem,
and you will appear there with your glory.
17 You will listen to the prayers of your people who are homeless,
and you will not ignore them
when they plead with you to help them.
18 Yahweh, I want to write these words
so that people in future years will know what you have done,
in order that people who are not born yet will praise you.
19 They will know that you looked down from your own place in heaven
and saw what was happening on the earth.
20 They will know that you hear prisoners groaning
and that you will set free those who have been told, "You will be executed."
21 As a result, people in Jerusalem will praise you for what you have done.
22 Many people from other people groups and those who are citizens of other kingdoms will gather to worship you.
23 But now you have caused me to become weak while I am still young;
I think that I will not remain alive very long.
24 I say to you, "My God, do not take me away from the earth now
before I become old!
You, on the other hand, live forever!
25 You created the world long ago,
and you made the heavens with your own hands.
26 The earth and the heavens will disappear, but you will remain.
They will wear out as clothes wear out.


Website Designer: Thelonius Lucien
<EMAIL_ADDRESS>You can also contact Alexander Lopez
Alexander.Lopex@sascholar.org


class: center, middle ### W4995 Applied Machine Learning # Evaluating Clustering 03/28/18 Andreas C. Müller ??? Last time we talked about clustering, and an obvious question is: Which algorithm do we use, and how do we set the parameters? That's a bit more of an art than a science, unfortunately. But I want to give you some tools to do better than just guessing. FIXME illustration of clustering picking up on different concepts? FIXME talk about http://datamining.rutgers.edu/publication/internalmeasures.pdf FIXME Calinski-Harabaz FIXME S_Dbw? https://pdfs.semanticscholar.org/dc44/df745fbf5794066557e52074d127b31248b2.pdf FIXME checkout https://www.coursera.org/learn/cluster-analysis/lecture/baJNC/6-5-external-measure-2-entropy-based-measures --- class: center, middle # Evaluation Measures ??? The fist thing I want to talk about is evaluation measures. There's basically two types: Supervised, where you have a ground-truth clustering, and unsupervised, where you don't. Let's start with the supervised methods. --- class: spacious #Supervised evaluation - Compare clustering against "Ground Truth". - Unclear what the assumptions are. - Impossible to do in practice. ??? - Usually classification datasets. - Often used for papers on clustering algorithms. - Compare partitions, not labels! Here you assume that you are provided some "true" cluster labels that you want to compare against. What people often do is just use a classification dataset, and basically treat it as an unsupervised clustering problem. This is particularly done by researchers in clustering that want to show that their algorithm performs better than some other algorithm. Doing this, you are measuring how well a completely unsupervised algorithm can identify the classes in the dataset as clusters. The problem here is that it's very unclear what kind of assumptions about the clusters you are making with this labeling of the data. There might be many valid ways to cluster a dataset, and it's unclear why the class labels should somehow correspond to the "best" clustering, or in what sense this clustering is "best". It's very unlikely that there's a coherent definition of cluster so that the classes correspond to clusters for any particular dataset - unless the classes are really well separated. The final downside is that this is actually impossible in practice, and therefore basically completely useless. Why would you use a clustering algorithm if you have ground truth? You'd always be better using a classification algorithm. Even if you have very little labeled data, using this in some way is probably still much better than trying to run a completely unsupervised clustering algorithm. So basically if you're running a supervised evaluation method, why are you doing clustering at all? This makes sense to do if you're studying clustering algorithms, but not really in any practical application. I still want to talk you through how these methods work. One thing to keep in mind for all the cluster evaluation metrics is that they compare partitions, not labellings. While clustering outcomes in scikit-learn (and other libraries) are always given as integers, you don't want to compare the actual integers, as you do in supervised learning. You want to compare the partitions given by the cluster labels. --- class:spacious # Thinking in Partitions - Partition: set expressed as a union of disjoint sets. - Y=[0, 0, 0, 1, 1, 1] → C={{0, 1, 2}, {3, 4, 5}} - Y=[0, 1, 0, 1, 2, 2] → C={{0, 2}, {1, 3}, {4, 5}} - Partition given by [1, 0, 1, 0] and [0, 1, 0, 1] are the same! ??? Here's a small example. Partitioning the data means expressing it as a union of disjoint sets. So a partitioning is a sett of sets. In this example, the labelling 0,0,0,1,1,1 means that the first three data points are in the same cluster, and the remaining datapoints are in a different cluster. And I wrote that down as a partition here, using the sample indices. Here's another example of 0,1,0,1,2,2, which means the zeroth and 2nd example are in one set, and the first and third sample are in a different set, and the fourth and fifth are in a different one again. So the labels really have no semantics, and the partitions given by 0,1,0,1 and 1,0,1,0 are the same, so if you compare these two clusterings, they should be regarded as exactly the same clustering. --- # Contingency Matrix For clusterings `$X_i$` and `$Y_j$`, `$n_{ij}=|X_i \cap Y_j|$` `$$ \begin{array}{c|cccc|c} {{} \atop X}\!\diagdown\!^Y & Y_1& Y_2& \ldots& Y_s& \text{Sums} \\ \hline X_1& n_{11}& n_{12}& \ldots& n_{1s}& a_1 \\ X_2& n_{21}& n_{22}& \ldots& n_{2s}& a_2 \\ \vdots& \vdots& \vdots& \ddots& \vdots& \vdots \\ X_r& n_{r1}& n_{r2}& \ldots& n_{rs}& a_r \\ \hline \text{Sums}& b_1& b_2& \ldots& b_s& \end{array}$$` ??? The most basic way to compare two different clustering results, say yours and the ground truth, or two different algorithms, is the contingency matrix. You might know contingency matrices from statistics, where they are used to relate discrete probability distributions. The Contingency matrix is quite similar to a confusion matrix, in that it counts how often a datapoint was given label i in one clustering and label j in the other clustering. So rows correspond to one clustering, and columns correspond to the other. However, because labels don't mean anything, the diagonal has no meaning, and both of these contingency matrices here mean that the two clusterings produce the same partition, i.e. they are identical. Also, contingency matrices do not need to be square. If you run clustering on a dataset, you don't usually know the number of clusters so you want to compare different clusterings with different numbers of clusters. FIXME needs example Now that we have this basic representation of cluster overlap, we can define derived metrics --- # Contingency Matrix - Both of these mean identical partitions: .center[ ![:scale 20%](images/contingency_matrix_1.png) ![:scale 20%](images/contingency_matrix_2.png) ] --- # Mutual Information `$$\Large MI(U,V)=\sum\limits_{i=1}^{|U|}\sum\limits_{j=1}^{|V|}P(i,j)\log\frac{P(i,j)}{P(i)P'(j)}$$` `$$\Large =\sum\limits_{i=1}^{|U|}\sum\limits_{j=1}^{|V|}\frac{|U_i \cap V_j|}{N} \log\frac{N|U_i \cap V_j|}{|U_i||V_j|}$$` ??? - Between 0 and 1 but not normalized or adjusted. If V is subpartition of U still = 1 A very common metric is mutual information. which is the mutual information of the discrete distributions given by the partitions (or the contingency table). FIXME unify notation (second equation only?) FIXME define P(i), P(i, j) Mutual information is a standard information theoretic measure for relating two distributions, providing a measure of how interdependent they are. There we just create distributions corresponding to the partitions and compare those. We can rewrite this uing the contingency matrix, the probability P(i, j) is just the the entry n_ij divided by the number of samples, and P(i) and P(j) are the row and column sums divided by the number of samples. Mutual information is also always between zero and one, with one being perfect information. However, if one partition is a sub-partitioning of the other partition, the mutual information is still one. --- #NMI and AMI `$$ NMI(U,V) = \frac{MI(U,V)}{\sqrt{H(U)H(V)}}$$` `$$ AMI = \frac{MI - E[MI]}{\max(H(U),H(V))-E[MI]} $$` ??? Similar to the rand index, the mutual information depends on the number of samples and size of the clusters, and it's common to adjust it for chance, to give it a more universal scale. Two common normalizations are the normalized mutual information NMI, which divides the mutual information by the squareroot of the products of the entropy of the marginal distributions, and the adjusted mutual information AMI, which has an adjustment for chance. FIXME definition of entropy? Both use the entropy to penalize overpartitioning, but only AMI is adjusted for chance, so zero on expecation. These two usually behave pretty similarly. There is other variants of NMI, for example dividing by the maximum, as I did here for AMI. - Penalizes overpartitioning (via entropy) - Adjusts for chance – any two random partitions have expected AMI of 0 FIXME definition of entropy? FIXME use max both times? FIXME explain expectation better! --- # Rand Index - Computed of partitions U, V over the same elements (say {0, …, n-1}) `$$ R(U, V) = \frac{r+s}{\binom{n}{2}} $$` - $r$ : Number of pairs of points in the same set in U and in V - $s$ : Number of pairs of points in the different sets in U and in V - $\binom{n}{2}$ : Number of possible pairs - Between 0 and 1 ??? One of the most useful one is the Rand Index. For two partitions C1 and C2, it computes the number of pairs points that are in the same set in C1 and in the same set in C2, called "a" here, and the number of pairs of points that are in different sets in both C1 and C2, called "b" here. This is divided by n choose 2, so the number of all possible pairs of points. This is always between zero and one, though the actual possible minimum and maximum depend on the number of clusters and the number of points. Let's do an example. We're comparing the labeling 0011 and the labelling 1100. They are the same, so hopefully the rand index is going to be 1. First, I write these as partitions here. Then I check which pairs of points are in the same set in both partitions. The pair 0,1 is in the same set in both partitions, so we count that. The pair 2,3 is also in the same set in both partitions, so we count that as well. No other pairs are in the same set in either partition, so we're done counting for "a". Now we count the pairs of points that are in different sets. Zero and 2 are in different sets in partition one, and also in different sets in partition 2. Same for zero and 3, and 1 and 2, and 1 and 3. So that's four pairs in total. So the nummerator is 2 + 4 = 6. The denominator is 4 choose 2, which is also six, so the rand index is 1, as expected. For a second example, let's look at these two partitions, 0,1,0,1 and 1,2,0,0. Here there's no pair of points that is in the same set in both of them. There are three pairs of points that are in different sets in both of them, (1,2), (2,3) and (0,3). So the rand index is 3/6, so 1/2. Alright, so the rand index is about matching pairs of points, which is slightly tricky to do in your head, but not that complicated really. --- # Rand Index Example `$$R([0,0,1,1],[1,1,0,0]) = R\left(\{\{0,1\},\{2,3\}\},\{\{0,1\},\{2,3\}\} \right)$$` `$$ = \frac{2+4}{6} = 1 $$` `$$R([0,1,0,1],[0,0,1,2]) = R(\{\{0,2\},\{1,3\}\},\{\{0,1\},\{2\},\{3\}\})$$` `$$ = \frac{0+3}{6} = \frac{1}{2} $$` ??? --- # Adjusted Rand Index (ARI) `$$ AdjustedIndex = \frac{\text{Index}-\text{ExpectedIndex}}{\text{MaxIndex}-\text{ExpectedIndex}}$$` `$$ARI = \frac{\sum_{ij}\binom{n_{ij}}{2} - \frac{\sum_{i}\binom{a_{i}}{2}\sum_{j}\binom{b_{j}}{2}}{\binom{n}{2}}} {\frac{1}{2}\left(\sum_{i}\binom{a_{i}}{2} + \sum_{j}\binom{b_{j}}{2}\right) -\frac{\sum_{i}\binom{a_{i}}{2} + \sum_{j}\binom{b_{j}}{2}}{\binom{n}{2}}}$$` ??? - Maximum of 1, 0 on expectation (can become negative!) [Don’t learn the formula] Because the range of the rand index depends on the data, in practice people prefer to use the adjusted rand index, which is adjusted for chance. So we take the index we just computed, subtract the expected index over this dataset, and divide it by the maximum index on this dataset minus the expected index. This means the expectation over all cluster assignments is zero, and the maximum over cluster assignments is 1. However, it means the adjusted rand index can become negative, basically if the partitions are less similar than they would be on expectation, or by chance. What this works out to is this nice formula here. The n_ij is the entries of the continency table we saw before, and the a_i and b_i are the row and columns sums of that contingency table. I really don't wanna remember that formula, so don't worry, it's not gonna be on the test. Just keep in mind that it measures exactly the same thing as the rand index, only adjusting for chance. You can see all of the normalization terms only depend on the row and column sums, so the number of samples in each of the clusters. Only this first part here depends on the contingency table. The most common use of this is to compare a particular clustering to a ground-truth clustering. --- # KMeans on digits dataset .center[![:scale 70%](images/ari_nmi_ami_digits.png)] ??? One of the most common applications is picking a number of clusters or tuning a clustering algorithm. So we could try to use the supervised methods to figure out what's the good number of clusters to cluster the digit dataset with KMeans. And here's the results. This is using adjusted rand index, adjusted mutual information and normalized mutual information. As you can see that they all have basically the same peak, interestingly, at 11. If you are in KMeans with 11, this will give us the best mutual information or the best rand index with respect to the ground truth labeling of the digits. You can also see that for the normalized mutual information, the over partitioning wasn't penalize that strongly. So basically, if you pick more and more clusters, it doesn't really degrade that much. While the adjusted rand index and adjusted mutual index, basically degrade more quickly so it’s very clear where the maximum is. Q: What are the two partitions I compare? One is KMeans and the other one is the class labels. --- class:center,middle #Unsupervised Evaluation ??? Now let's talk about unsupervised evaluation. So these are often a bit vaguer because you need to define what is a good clustering given the data and the partition. I find them a little bit less helpful. But you can actually use them in practice. --- # Silhouette Score - For each sample: `$$ s = \frac{b-a}{\max(a,b)} $$` - a is the mean distance to samples in same cluster - b is the mean distance to samples in nearest cluster - For whole clustering: average over all samples - Prefers compact clusters (like k-means) ??? One of the classical ones is the silhouette score. The silhouette score for each sample, you compute as the mean distance to samples in the nearest cluster minus the mean distance of two samples in the same cluster. This score can be computed for every data point. If you want to do is for the whole partition, you just average this over all the samples. One of the downsides of this is that it prefers compact clusters and not complex cluster shapes. Basically, KMeans will do usually pretty good at this whereas DBSCAN will not. --- .center[ ![:scale 60%](images/ari_silhoutte.png)] ??? Here, the ARI is comparing to the ground truth where the ground truth is one-half moon versus the other half moon, they are two different labels. And the silhouette score is completely unsupervised and measures the compactness of the cluster in a sense. And you can see that they basically completely disagree. In the top left, since the clusters are quite compact, the silhouette score is the highest of the bunch, even though there’s one cluster that spans both of the half moons. Whereas the one in the bottom right, recovers the ground truth and so the ARI is very high. But the silhouette score is really low, it says this is the worst clustering. Silhouette score will not be able to pick up on these more complex cluster shapes, it will always favor more compact clusters. Depending on your application that might be okay or might not be okay. --- .center[ ![:scale 80%](images/number_clusters.png)] - What's the right number of clusters? ??? This is kind of a very trivial example. But still, in higher dimensions, it might not be as obvious. So how many clusters are there? It’s drawn from four Gaussians. And actually, instead of just looking at the silhouette score, average over everything, I can look at the individual silhouette score of the points. --- # Silhouette Plots .center[ ![:scale 70%](images/silhouette_plots.png) ] - Plot sorted silhouette score for each sample in each cluster - http://scikit-learn.org/stable/auto_examples/cluster/plot_kmeans_silhouette_analysis.html ??? Here at the top, I have KMeans which runs with eight clusters. The thing that people often use is these silhouette plots. These basically plot the silhouette score for each point in each of the clusters. People found these diagrams help them to figure out if it's a good clustering or not. What you're looking for is, you want to have these things have like a smooth curve, which basically indicates, most of the points have a very high silhouette score and then there's some couple that is sort of on the fringes of the cluster. If you see something that’s not so smoothly curved then it indicates, there's a couple of samples with very high silhouette score, then there's a sudden drop, and then there are some more points. This might also indicate that there are maybe two clusters within one cluster, which is actually the case here. And if you over partition your data, you might get something like the red one in the fourth diagram, where you don't have a nice curve, indicating that the red has a very small silhouette score compared to the other ones and also, they basically fall off very quickly. --- .center[ ![:scale 60%](images/number_clusters_gaussians.png) ] ??? So how does it look like if we have a more complicated dataset. So this is generated as a mixture of 10 Gaussians, that doesn't mean that 10 is a good clustering. I think probably 5 would be my best guess. But it's really there's not really a right answer. There's not very clear distinct clusters. --- .center[ ![:scale 60%](images/more_silhouette_plots.png) ] ??? We can also look at the silhouette score for this. Looking at these, it's very hard to say, what is a good clustering. Q: If the score can’t help us why are we using it? Because there's no better score. Question is, is this something that we care about? Is this something that's going to happen in practice? If you say yes, that this is something that you care about and you want to recover these 2 moons, then never ever use this. --- # Digits Dataset .center[ ![:scale 80%](images/silhouette_digits.png) ] ??? Here's the same thing for the digit data set. And of the ones that I tried, 15 clusters is the best. But again, by looking at the diagrams they're not very helpful to me. It definitely can show you the density of the cluster, in some sense. --- # Cluster Stability - Try multiple random samplings / perturbations - Measure consistency - Clustering stability: an overview (U. v. Luxburg) https://arxiv.org/abs/1007.1075 ??? There's one other measure that I want to talk about called cluster stability. Cluster stability is not really a simple measure of how good is a partition, but it's more of how good is a particular parameter setting, or how good is a particular algorithm. The idea behind cluster stability is that if you changed your data a little bit and your clustering is good then it should be the same. So basically, you're trying to add some noise, the desired method is to do bootstrap sampling or you could add like random noise to the data or something like that, and ideally, if there are actually good clusters in the data, doing bootstrap sampling or adding noise to the data, shouldn't really influence how the data is partitioned by your clustering. So basically, you're not looking at a single partition or comparing two partitions, instead you're looking at an algorithm and you're adding noise into the algorithm, and you're figuring out are the results stable. If the results are stable, it's good, if the results are not stable, it's not so good. - For comparing clustering algorithms / parameters (with different number of clusters for example) - Try multiple random samplings / perturbations - The configuration that yields the most consistent result among perturbations is best. --- # Stability Intuition .center[ ![:scale 80%](images/stability_intuition.png) ] ??? If we want to pick the number of clusters, for example. This is from the paper that I previously quoted. Let's say, you have these four different clusters. The dotted line is clustering the algorithm found. And so let's say I tried the algorithm trying to find two clusters. It might divide horizontally or vertically, so it's not going to be very stable. If I pick two large number of clusters, it will probably find the four clusters, but then it will randomly split up one of them. But it will split up a different one every time and so this will also not be stable. But if I asked for four clusters, the four clusters will always be the same independent of a bootstrap sampling or noise that was added to the data. --- .smaller[ ```python def cluster_stability(X, est, n_iter=20, random_state=None): labels = [] indices = [] for i in range(n_iter): # draw bootstrap samples, store indices sample_indices = rng.randint(0, X.shape[0], X.shape[0]) indices.append(sample_indices) est = clone(est) if hasattr(est, "random_state"): # randomize estimator if possible est.random_state = rng.randint(1e5) X_bootstrap = X[sample_indices] est.fit(X_bootstrap) # store clustering outcome using original indices relabel = -np.ones(X.shape[0], dtype=np.int) relabel[sample_indices] = est.labels_ labels.append(relabel) scores = [] for l, i in zip(labels, indices): for k, j in zip(labels, indices): # we also compute the diagonal which is a bit silly in_both = np.intersect1d(i, j) scores.append(adjusted_rand_score(l[in_both], k[in_both])) return np.mean(scores) ```] ??? You need to have your dataset and your model. So you're repeatedly fitting the clustering algorithm to do this. I want to make sure that I do this in the order of the original dataset. So for each original sample, either I have minus one, if it wasn't part of the bootstrap, or have the cluster labeled if it was a part. And then now for each pair of clusters that I did, I look at the samples that are both bootstrap samples and the intersection of indices. And then I look at, in this case, adjusted rand score between the two partitions. And then, in the end, I compute the mean over all these pairs of partitions. So here, I created a bunch of partitions randomly and I computed the rand score between all pairs of the partitions and then I computed mean. The idea is, if they are very consistent, then the adjusted rand score between all different pairs will be high and so the final number will be high. FIXME too complex to show --- .center[![:scale 70%](images/cluster_stability.png)] - Outcome depends critically on initialization and n_iter. - This is kmeans++ with n_iter=10 ??? Here, I look at KMeans with 10 clusters. You can actually see that the cluster stability here is 1 for the three in the top row because points in each of them are closer together than the other points. But then if I do five or more clusters it becomes less stable. This depends on how I initialized my KMeans.  If I do random initialization and don't do restarts, then everything will be slightly less stable because of the randomness of KMeans. --- .center[![:scale 80%](images/cluster_stability_2.png)] ??? You can see that two is basically the stable cluster. But this is how data looks like in the real world. And you never know what the answer should be. That's why I think it's interesting to look at how it behaves for relatively random data. --- # Digits Dataset .center[ ![:scale 70%](images/digits_dataset_ncluster_scan.png) ] ??? Here is the digit dataset looking at different scores. You can see that for ARI is 12, stability is 8 and for silhouette score, it's 16. You can see that the silhouette score is pretty noisy. The stability goes down pretty rapidly if you add too many clusters. ARI is supervised while the other two methods are unsupervised. --- # Stability on Digits Dataset .center[ ![:scale 70%](images/cluster_stability_different_algos.png ) ] ??? Here, I'm looking at stability for different clustering algorithms. DBSCAN looks a little bit weird since there's no number of clusters in DBSCAN and so what I actually scanned was the epsilon. And if you set epsilon very high, say 2, then everything will be in the same cluster. If you set it to 1.8, there will be only one cluster and a bunch of noise points and so on. If I set it to 1.4, it’s starting to be two clusters, and so on. If I set it to too small then there will be mostly noise points. So if I set it 0.029, there's going to be one cluster, and the rest will be labeled as noise. Overall, I probably like KMeans the best. It has the same score (10) as agglomerative at its peak, which is a reasonable score. DBSCAN doesn't seem to be able to find any stable clusters with any of the settings. --- # Adult Dataset .center[ ![:scale 70%](images/adult_dataset_ncluster_scan.png) ] ??? This is the adult dataset stating if adults are making more or less than 50k a year. This is not very nice to cluster since it has a lot of categorical data. I ran KMeans and KMeans probably will not do that well on this kind of data. But what I found interesting is that the silhouette score is very noisy and goes up with more clusters, whereas ARI and stability go down. Here, I like the results of stability much more than the results of silhouette. --- # Labeled Faces in the Wild .center[![:scale 70%](images/labfaces_ncluster_scan.png)] - With PCA, 100 components. Without whitening: no stable clusters! ??? Since this dataset is quite high dimensional, I'm using PCA with 100 components and then doing the whitening. If I don't do whitening, there is no clustering, basically, that works. So if you try to cluster the original input space, there are no stable clusters. I mean, this is also something I can find out by using the stability score, for example. After computing, I can see that the ARI and stability behave similarly while the silhouette score is quite different. --- class:spacious #Qualitative Evaluation(aka eyeballing) - Look at low-dim visualization - Look at individual points - Look at cluster centers (if available) ??? Eyeballing the data is the most important thing about clustering. Supervised and unsupervised scores sometimes can be helpful, but I think what you really usually want out of clustering is doing explorative data analysis. So if I use my stability score and find five is the perfect number for KMeans on a dataset, what does that tell me? Probably not that much. What I'm interested in is, what are the five clusters in my dataset and do they mean something? The things that I usually do is look at low dimensional visualizations, look at individual points and look at cluster centers if you’re doing KMeans. So that's what I want to do here. --- # Digits .center[ ![:scale 100%](images/digits_eyeball.png) ] ??? I think here I picked the best parameters, according to the graph that I showed you, for DBSCAN, agglomerative clustering, and KMeans. I think this is quite informative, because I can see that DBSCAN doesn't find the things that I think would be the clusters and there's like a big orange cluster that’s all over the place and the PCA is also all over the place while agglomerative and KMeans are more localized, both in the PCA and in the t-SNE. Unlike DBSCAN, Agglomerative and KMeans seem to be doing something reasonable, that I can look into more. --- # K-Means cluster centers .center[ ![:scale 70%](images/kmeans_cluster_centers_digits.png) ] .smaller[ ```python fig, axes = plt.subplots(2, 5, subplot_kw={'xticks': (), 'yticks': ()}, figsize=(10, 5)) km = KMeans(n_clusters=10).fit(digits.data) for ax, center in zip(axes.ravel(), km.cluster_centers_): ax.imshow(center.reshape(8, 8), cmap='gray_r') fig.suptitle("K-Means cluster centers") ```] ??? One of the things I can look for KMeans is to look at the cluster centers. So, unfortunately, for the homework, this will not be as nice. For the digits, I can just look at the centers, I can plot them and therefore, I can visualize individual points in a meaningful way. If you have a 100-dimensional feature vector usually you cannot visualize them in a meaningful way. This is not usually what you can do. But here, in this case, where we have these images, we can look at the cluster centers and they actually seem to be quite natural. Apart from the two in the last column, the rest of them looks pretty clear. So this looks like a reasonable clustering. And it seems to have reasonably discovered the digits and the data themselves. --- # Agglomerative Clusters .center[ ![:scale 90%](images/agglomerative_clusters.png) ] ??? So for agglomerative clustering and DBSCAN, you can't do that, because there are no cluster centers. So what I like to do instead is just pick random points from the clusters. In this example with 10 clusters, each column corresponds to a cluster and the rows are just random samples from the cluster. And so now I can, again, maybe try to semantically identify these clusters. And most of these look pretty natural to me. - Each column corresponds to one of 10 clusters, “first” 5 samples are shown. - Number on top gives clusters size --- # DBSCAN Clusters .center[ ![:scale 90%](images/dbscan_clusters.png) ] ??? I did the same thing for DBSCAN and I showed the number of points in this cluster. But here, you can see this is like a very specialized clustered, it only has 27 samples on it. This is also a very specialized cluster since it has the 7s with the hook on the front. The third column, which has nearly 1000 data points, clearly has all different digits in it. So this is not very satisfactory. Basically, I looked at three different algorithms, looked at three different parameters, looked at stability scores, and looked at 2D visualizations and data points. So now, I have a relatively good idea what these three algorithms did. This was basically for all for exploratory data analysis. --- #For feature extraction .smaller[ ```python from sklearn.linear_model import LogisticRegression from sklearn.model_selection import GridSearchCV km = KMeans(n_init=1, init="random") pipe = make_pipeline(km, LogisticRegression()) param_grid = {'kmeans__n_clusters': [10, 50, 100, 200, 500]} grid = GridSearchCV(pipe, param_grid, cv=5, verbose=True) grid.fit(X, y_people) ```] .center[ ![:scale 40%](images/param_kmeans_nclusters.png) ] ??? If you want to do feature extraction, if you want to use clustering in a supervised pipeline, this is much easier. So here's an example of using clustering in a supervise pipeline on the faces dataset. Here, I'm trying to classify faces into different people. And so what I'm doing is, I use KMeans as a feature extractor. So I make a pipeline of KMeans and logistic regression and then I can just grid search number of clusters on KMeans because since now I have an actual evaluation criterion, I want to have an accurate model. I don't care about finding semantic clusters, I only care about finding an accurate model. And so if I actually was going to use KMeans as a part of the supervised pipeline, I can throw all these metrics and all the introspection out of the window and I can just actually measure what I'm interested in. This is often what you want to do, often you want to have as part of a bigger pipeline and then you can just measure the whole pipeline. So here in this case, basically, as soon as I use 200 clusters, everything is good. The stability score told me that I will get the most stable clustering with like 40 or 50, but we don't really care about how stable they are. We just want a good feature space. And so if I use 200, or 500 or 1000 clusters, it's going to be fine. --- class: spacious # So what is n_clusters? - As preprocessing – The one that makes the whole pipeline work best. – Larger is often better. - For exploratory analysis: – The one that tells you the most about the data. - For most datasets, there is no “correct” number of clusters. ??? The question is, what is the number of clusters, how do you set the parameters, how do you pick and it really depends a lot on what your ultimate goal is. The two goals that I talked about, mostly it was for exploratory data analysis. And then you should do whatever it tells you most about the data. And we have these different metrics and different ways to see if it actually tells something about the data. For preprocessing, just fine tune your whole pipeline. Usually, the more is better, as you can see here, basically, adding more and more clusters, gives me more and more features and reasonable better results. And in the end, there's usually no correct number of clusters. If you want clusters that are interpretable, I think having stable clusters is something that makes a lot of sense. If you want to introspect them, you would want them to be stable. If you ran clustering and you get the results and then you go in and do all the manual analysis that I just talked about, and you look at the samples and so on, and then you run clustering again, and it gives you completely different results, then what would the results even mean? So I think, looking for stability if you want to interpretable clustering is like a reasonable thing to do. --- class:center,middle If you want semantics from clustering, you need manual confirmation. Clustering might not pick up on what you thought it would. ??? Finally, if you want any semantics from your clustering, you always need to look at the data, even on the digits, I found exactly 10 clusters. No one tells me you have to right 10 clusters. If I want to know if I’ve actually found the digits I need to look at the cluster centers and look at the data points as I did before and I need to identify what does each cluster correspond to and do what do they mean and that's the only way to actually extract any semantic information. --- class: middle # Questions ?


read_dct.Rd
Read dCT data from qPCR experiment.
read_dct(file, format = "csv", relevel = NULL, ...)
| file | File name of dCT data in working directory, or, alternatively, path to the qPCR data file. |
|---|---|
| format | Format of the data file to import. Either |
| relevel | Optional named list of character vectors, where names represent factors and each character vector represents the corresponding appropriate level. |
| ... | Additional arguments to be passed to |
qPCR S4 object containing dCT data to be passed to next functions.
Ayush Noori


--- layout: page title: 404 - Page Not Found ---
Sorry I could not find what you are looking for! Please see my recent posts..


class: center, middle, title-slide count: false # Lesson 4 ##Convolutional neural networks ## ... writing a PyTorch module
.bold[Marc Lelarge] --- # Deep Learning pipeline ## Dataset and Dataloader + .red[Model] + .grey[Loss and Optimizer] = .grey[Training] .center[
] --- # Overview of the course: 1- .grey[Course overview: machine learning pipeline] 2- .grey[PyTorch tensors and automatic differentiation] 3- .grey[Classification with deep learning] 4- Convolutional neural networks * .red[Building block for a model] - in PyTorch: [Convolutional layrers](https://pytorch.org/docs/stable/nn.html#convolution-layers) and [Pooling layers](https://pytorch.org/docs/stable/nn.html#pooling-layers) in `torch.nn` --- # Writing a PyTorch module We only built once a Neural Network from scratch in [lesson 2](https://github.com/mlelarge/dataflowr/blob/master/PlutonAI/02_basics_PlutonAI_colab.ipynb) ``` model = torch.nn.Sequential(torch.nn.Linear(2, 1)) ``` -- count: false A Multilayer perceptron (MLP) with a 2 dimension input, 1 dimension output, ReLU activation and one hidden layers of dimensions 50 and a final sigmoid layer can be written as: ``` import torch.nn as nn model = torch.nn.Sequential(nn.Linear(2, 50), nn.ReLU(), nn.Linear(50,1), nn.Sigmoid()) ``` -- count: false But for any model of reasonable complexity, the best is to write a sub-class of `torch.nn.Module`. --- # Writing a PyTorch module To create a module, one has to inherit from the base class `torch.nn.Module` and implement the constructor `__init__(self, ...)` and the forward pass `forward(self, x)`. Here is the code for our last model: ``` import torch import torch.nn as nn *import torch.nn.functional as F class MLP(nn.Module): def __init__(self): super(MLP, self).__init__() self.fc1 = nn.Linear(in_features=2, out_features=50) self.fc2 = nn.Linear(in_features=50, out_features=1) def forward(self,x): x = self.fc1(x) * x = F.relu(x) x = self.fc2(x) * return F.sigmoid(x) ``` [difference between `nn.ReLU()` and `F.relu`](https://discuss.pytorch.org/t/whats-the-difference-between-nn-relu-vs-f-relu/27599/2) --- # Writing a PyTorch module Inheriting from `torch.nn.Module` provides many mechanisms implemented in the superclass. First, the `(...)` operator is redefined to call the `forward(...)` method and run additional operations, see [ref](https://discuss.pytorch.org/t/any-different-between-model-input-and-model-forward-input/3690). The forward pass should be executed through this operator and not by calling forward explicitly. ``` model = MLP() input = torch.empty(12, 2).normal_() output = model(input) ``` -- count: false All `Parameters` added as class attributes are seen by `Module.parameters()`. As long as you use autograd-compliant operations, the backward pass is implemented automatically. This is crucial to allow the update of the `Parameters` with your optimizer. --- # Writing a PyTorch module Define a loss and an optimizer as before: ``` loss_fn = nn.BCELoss() optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate) ``` -- count: false One step of gradient descent is done as before: ``` output = model(x) loss = loss_fn(output, label) optimizer.zero_grad() loss.backward() optimizer.step() ``` So now is time to construct more complex architectures! --- class: end-slide, center count: false The end.


Privacy policy of COVID-19 Swiss numbers Lametric statistics app.
This application requires an Internet connection to retrieve the data that will be displayed within the
application. However, we agree not to collect any personal information of any kind.


(Your Weather For Five)
Gives you quick access to your upcoming five day forecast. We just need your location.


Members
HEIGHT
This variable keeps track of the scale of the world for the objects in the simulation.
NUMBER_OF_VERTICES
This variable keeps track of the scale of the world for the objects in the simulation.
ORIGIN_X
This variable keeps track of the scale of the world for the objects in the simulation.
ORIGIN_Y
This variable keeps track of the scale of the world for the objects in the simulation.
SLOPE1
This variable keeps track of the scale of the world for the objects in the simulation.
SLOPE2
This variable keeps track of the scale of the world for the objects in the simulation.
SLOPE3
This variable keeps track of the scale of the world for the objects in the simulation.
SLOPE4
This variable keeps track of the scale of the world for the objects in the simulation.
SLOPE5
This variable keeps track of the scale of the world for the objects in the simulation.
TILE_DENSITY
This variable keeps track of the scale of the world for the objects in the simulation.
TILE_FRICTION
This variable keeps track of the scale of the world for the objects in the simulation.
TILE_RESTITUTION
This variable keeps track of the scale of the world for the objects in the simulation.
TILES_PER_PART
This variable keeps track of the number of tiles per part of the road.
WIDTH
This variable keeps track of the scale of the world for the objects in the simulation.
Methods
connecttile()
This method connects the tiles to each other in a sequential fashion starting from the first tile at the origin.
The road is split into 5 components, each with a different slope.
An array of random numbers is used in order to create the illusion of randomness, while enforcing comparability between cars of different
generations.
createtile(point1X, point1Y, point2X, point2Y, point3X, point3Y, point4X, point4Y)
This method creates a tile for the road.
Parameters:
| Name | Type | Description |
|---|---|---|
point1X |
Integer | The x-coordinate of the upper right hand vertex |
point1Y |
Integer | The y-coordinate of the upper right hand vertex |
point2X |
Integer | The x-coordinate of the lower right hand vertex |
point2Y |
Integer | The y-coordinate of the lower right hand vertex |
point3X |
Integer | The x coordinate of the lower left hand vertex |
point3Y |
Integer | The y-coordinate of the lower left hand vertex |
point4X |
Integer | The x-coordinate of the upper left hand vertex |
point4Y |
Integer | The y-coordinate of the upper left hand vertex |