Cloudera beschleunigt die KI-Industrialisierung mit Cloud nativer Machine-Learning-Plattform

Neues Cloudera-Angebot vereinfacht Machine-Learning-Workflows mit einer einheitlichen Erfahrung für Data Engineering und Data Science auf Kubernetes.

München, Palo Alto (Kalifornien), 5. Dezember 2018 – Cloudera, Inc. (NYSE: CLDR) hat eine Vorschau auf eine neue, Cloud-basierte Machine-Learning-Plattform der nächsten Generation auf Basis von Kubernetes veröffentlicht. Das kommende Cloudera Machine Learning erweitert das Angebot von Cloudera für Self-Service Data Science im Unternehmen. Es bietet eine schnelle Bereitstellung und automatische Skalierung sowie eine containerisierte, verteilte Verarbeitung auf heterogenen Rechnern. Cloudera Machine Learning gewährleistet auch einen sicheren Datenzugriff mit einem einheitlichen Erlebnis in lokalen, Public-Cloud- und hybriden Umgebungen.

Im Gegensatz zu Data-Science-Tools, die nur Teile des Machine-Learning-Workflows adressieren oder nur für die Public Cloud verfügbar sind, kombiniert Cloudera Machine Learning Data Engineering und Data Science, auf beliebigen Daten und überall. Darüber hinaus werden Datensilos aufgelöst, um den kompletten Machine-Learning-Workflow zu vereinfachen und zu beschleunigen. Unternehmen können ab sofort hier Zugang zu einer Vorabversion von Cloudera Machine Learning anfragen.

Container und das Kubernetes-Ökosystem ermöglichen die Agilität der Cloud in verschiedenen Umgebungen mit einer konsistenten Erfahrung und ermöglichen die Bereitstellung skalierbarer Services für die IT in hybriden und Multi-Cloud-Implementierungen. Gleichzeitig sind Unternehmen bestrebt, komplette Machine-Learning-Workflows zu operationalisieren und zu skalieren. Mit Cloudera Machine Learning können Unternehmen Machine Learning von der Forschung bis zur Produktion beschleunigen. Benutzer sind in der Lage, Umgebungen einfach bereitzustellen und Ressourcen zu skalieren und müssen so weniger Zeit für die Infrastruktur und können mehr Zeit für Innovationen aufwenden.

Zu den Fähigkeiten gehören:

  • Nahtlose Portierbarkeit über Private Cloud, Public Cloud und Hybrid Cloud auf Basis von Kubernetes.

  • Schnelle Cloud-Bereitstellung und automatische Skalierung.

  • Skalierbares Data Engineering und Machine Learning mit nahtloser Abhängigkeitsverwaltung durch containerisiertes Python, R und Spark-on-Kubernetes.

  • Hochgeschwindigkeits-Deep-Learning mit verteiltem GPU-Scheduling und Training.

  • Sicherer Datenzugriff über HDFS, Cloud Object Stores und externe Datenbanken hinweg.

„Teams produktiver zu machen, ist entscheidend für die Skalierung von Machine Learning im Unternehmen. Modelle konsistent über eine hochskalierbare, transparente Infrastruktur zu erstellen und einzusetzen und dabei überall auf Daten zuzugreifen, erfordert aber eine neuartige Plattform”, sagt Hilary Mason, General Manager, Machine Learning bei Cloudera. „Cloudera Machine Learning vereint die kritischen Funktionen von Data Engineering, kollaborativer Exploration, Modelltraining und -bereitstellung in einer Cloud-basierten Plattform, die dort läuft, wo Sie sie benötigen – mit den integrierten Sicherheits-, Governance- und Managementfunktionen, die unsere Kunden nachfragen.”

„Bei Akamai haben wir ausgereifte Web-Sicherheitssysteme auf der Grundlage einer umfassenden Datenanalyse und -verarbeitung aufgebaut. Dabei ist uns bewusst geworden, dass Geschwindigkeit und Skalierbarkeit entscheidend für die Erkennung von Anomalien im Internet sind”, sagt Oren Marmor, DevOps Manager, Web Security bei Akamai. „Die Agilität, die Docker und Kubernetes Apache Spark verleihen, ist für uns ein wichtiger Baustein, sowohl für Data Science als auch für Data Engineering. Wir freuen uns sehr über die Einführung der kommenden Cloudera Machine Learning Plattform. Die Möglichkeit, mit der Plattform das Abhängigkeitsmanagement von Betriebssystemen und Bibliotheken zu vereinfachen, ist eine vielversprechende Entwicklung.”


Matt Brandwein, Senior Director of Products bei Cloudera, erläutert im Video, wie die neue Cloudera Plattform Teams in die Lage versetzt, Machine Learning im Unternehmen zu entwickeln und einzusetzen.

Mit Cloudera Machine Learning sowie der Forschung und fachkundigen Beratung durch die Cloudera Fast Forward Labs bietet Cloudera einen umfassenden Ansatz zur Beschleunigung der Industrialisierung von KI.

Um Kunden dabei zu unterstützen, KI überall zu nutzen, hat das Applied Research Team von Cloudera kürzlich Federated Learning eingeführt, um Machine-Learning-Modelle von der Cloud bis zum Edge einzusetzen, gleichzeitig den Datenschutz zu gewährleisten und den Aufwand für die Netzwerkkommunikation zu reduzieren. Der Bericht bietet eine detaillierte, technische Erläuterung des Ansatzes sowie praktische technische Empfehlungen, die sich mit Anwendungsfällen in den Bereichen Mobilfunk, Gesundheitswesen und Fertigung befassen, einschließlich IoT-gesteuerter Predictive Maintenance.

„Federated Learning beseitigt Hindernisse für die Anwendung von Machine Learning in stark regulierten und wettbewerbsorientierten Branchen. Wir freuen uns sehr, unseren Kunden helfen zu können, damit Starthilfe für die Industrialisierung der KI zu erhalten”, so Mike Lee Williams, Forschungsingenieur bei Cloudera Fast Forward Labs.


Mike Lee Williams, Research Engineer bei den Cloudera Fast Forward Labs, erklärt im Video, wie Machine-Learning-Systeme mit Hilfe von Federated Learning ohne direkten Zugriff auf Trainingsdaten aufgebaut werden können. 

Über Cloudera

Bei Cloudera glauben wir, dass Daten morgen Dinge ermöglichen werden, die heute noch unmöglich sind. Wir versetzen Menschen in die Lage, komplexe Daten in klare, umsetzbare Erkenntnisse zu transformieren. Wir sind die moderne Plattform für Machine Learning und Analysen, optimiert für die Cloud. Die größten Unternehmen der Welt vertrauen Cloudera bei der Lösung ihrer herausforderndsten, geschäftlichen Probleme. Weitere Informationen finden Sie unter de.cloudera.com/.

Sentiment Analysis of IMDB reviews

Sentiment Analysis of IMDB reviews

This article shows you how to build a Neural Network from scratch(no libraries) for the purpose of detecting whether a movie review on IMDB is negative or positive.

Outline:

  • Curating a dataset and developing a "Predictive Theory"

  • Transforming Text to Numbers Creating the Input/Output Data

  • Building our Neural Network

  • Making Learning Faster by Reducing "Neural Noise"

  • Reducing Noise by strategically reducing the vocabulary

Curating the Dataset

In [3]:
def pretty_print_review_and_label(i):
    print(labels[i] + "\t:\t" + reviews[i][:80] + "...")

g = open('reviews.txt','r') # features of our dataset
reviews = list(map(lambda x:x[:-1],g.readlines()))
g.close()

g = open('labels.txt','r') # labels
labels = list(map(lambda x:x[:-1].upper(),g.readlines()))
g.close()

Note: The data in reviews.txt we're contains only lower case characters. That's so we treat different variations of the same word, like The, the, and THE, all the same way.

It's always a good idea to get check out your dataset before you proceed.

In [2]:
len(reviews) #No. of reviews
Out[2]:
25000
In [3]:
reviews[0] #first review
Out[3]:
'bromwell high is a cartoon comedy . it ran at the same time as some other programs about school life  such as  teachers  . my   years in the teaching profession lead me to believe that bromwell high  s satire is much closer to reality than is  teachers  . the scramble to survive financially  the insightful students who can see right through their pathetic teachers  pomp  the pettiness of the whole situation  all remind me of the schools i knew and their students . when i saw the episode in which a student repeatedly tried to burn down the school  i immediately recalled . . . . . . . . . at . . . . . . . . . . high . a classic line inspector i  m here to sack one of your teachers . student welcome to bromwell high . i expect that many adults of my age think that bromwell high is far fetched . what a pity that it isn  t   '
In [4]:
labels[0] #first label
Out[4]:
'POSITIVE'

Developing a Predictive Theory

Analysing how you would go about predicting whether its a positive or a negative review.

In [5]:
print("labels.txt \t : \t reviews.txt\n")
pretty_print_review_and_label(2137)
pretty_print_review_and_label(12816)
pretty_print_review_and_label(6267)
pretty_print_review_and_label(21934)
pretty_print_review_and_label(5297)
pretty_print_review_and_label(4998)
labels.txt 	 : 	 reviews.txt

NEGATIVE	:	this movie is terrible but it has some good effects .  ...
POSITIVE	:	adrian pasdar is excellent is this film . he makes a fascinating woman .  ...
NEGATIVE	:	comment this movie is impossible . is terrible  very improbable  bad interpretat...
POSITIVE	:	excellent episode movie ala pulp fiction .  days   suicides . it doesnt get more...
NEGATIVE	:	if you haven  t seen this  it  s terrible . it is pure trash . i saw this about ...
POSITIVE	:	this schiffer guy is a real genius  the movie is of excellent quality and both e...
In [41]:
from collections import Counter
import numpy as np

We'll create three Counter objects, one for words from postive reviews, one for words from negative reviews, and one for all the words.

In [56]:
# Create three Counter objects to store positive, negative and total counts
positive_counts = Counter()
negative_counts = Counter()
total_counts = Counter()

Examine all the reviews. For each word in a positive review, increase the count for that word in both your positive counter and the total words counter; likewise, for each word in a negative review, increase the count for that word in both your negative counter and the total words counter. You should use split(' ') to divide a piece of text (such as a review) into individual words.

In [57]:
# Loop over all the words in all the reviews and increment the counts in the appropriate counter objects
for i in range(len(reviews)):
    if(labels[i] == 'POSITIVE'):
        for word in reviews[i].split(" "):
            positive_counts[word] += 1
            total_counts[word] += 1
    else:
        for word in reviews[i].split(" "):
            negative_counts[word] += 1
            total_counts[word] += 1

Most common positive & negative words

In [ ]:
positive_counts.most_common()

The above statement retrieves alot of words, the top 3 being : ('the', 173324), ('.', 159654), ('and', 89722),

In [ ]:
negative_counts.most_common()

The above statement retrieves alot of words, the top 3 being : ('', 561462), ('.', 167538), ('the', 163389),

As you can see, common words like "the" appear very often in both positive and negative reviews. Instead of finding the most common words in positive or negative reviews, what you really want are the words found in positive reviews more often than in negative reviews, and vice versa. To accomplish this, you'll need to calculate the ratios of word usage between positive and negative reviews.

The positive-to-negative ratio for a given word can be calculated with positive_counts[word] / float(negative_counts[word]+1). Notice the +1 in the denominator – that ensures we don't divide by zero for words that are only seen in positive reviews.

In [58]:
pos_neg_ratios = Counter()

# Calculate the ratios of positive and negative uses of the most common words
# Consider words to be "common" if they've been used at least 100 times
for term,cnt in list(total_counts.most_common()):
    if(cnt > 100):
        pos_neg_ratio = positive_counts[term] / float(negative_counts[term]+1)
        pos_neg_ratios[term] = pos_neg_ratio

Examine the ratios

In [12]:
print("Pos-to-neg ratio for 'the' = {}".format(pos_neg_ratios["the"]))
print("Pos-to-neg ratio for 'amazing' = {}".format(pos_neg_ratios["amazing"]))
print("Pos-to-neg ratio for 'terrible' = {}".format(pos_neg_ratios["terrible"]))
Pos-to-neg ratio for 'the' = 1.0607993145235326
Pos-to-neg ratio for 'amazing' = 4.022813688212928
Pos-to-neg ratio for 'terrible' = 0.17744252873563218

We see the following:

  • Words that you would expect to see more often in positive reviews – like "amazing" – have a ratio greater than 1. The more skewed a word is toward postive, the farther from 1 its positive-to-negative ratio will be.
  • Words that you would expect to see more often in negative reviews – like "terrible" – have positive values that are less than 1. The more skewed a word is toward negative, the closer to zero its positive-to-negative ratio will be.
  • Neutral words, which don't really convey any sentiment because you would expect to see them in all sorts of reviews – like "the" – have values very close to 1. A perfectly neutral word – one that was used in exactly the same number of positive reviews as negative reviews – would be almost exactly 1.

Ok, the ratios tell us which words are used more often in postive or negative reviews, but the specific values we've calculated are a bit difficult to work with. A very positive word like "amazing" has a value above 4, whereas a very negative word like "terrible" has a value around 0.18. Those values aren't easy to compare for a couple of reasons:

  • Right now, 1 is considered neutral, but the absolute value of the postive-to-negative rations of very postive words is larger than the absolute value of the ratios for the very negative words. So there is no way to directly compare two numbers and see if one word conveys the same magnitude of positive sentiment as another word conveys negative sentiment. So we should center all the values around netural so the absolute value fro neutral of the postive-to-negative ratio for a word would indicate how much sentiment (positive or negative) that word conveys.
  • When comparing absolute values it's easier to do that around zero than one.

To fix these issues, we'll convert all of our ratios to new values using logarithms (i.e. use np.log(ratio))

In the end, extremely positive and extremely negative words will have positive-to-negative ratios with similar magnitudes but opposite signs.

In [59]:
# Convert ratios to logs
for word,ratio in pos_neg_ratios.most_common():
    pos_neg_ratios[word] = np.log(ratio)

Examine the new ratios

In [14]:
print("Pos-to-neg ratio for 'the' = {}".format(pos_neg_ratios["the"]))
print("Pos-to-neg ratio for 'amazing' = {}".format(pos_neg_ratios["amazing"]))
print("Pos-to-neg ratio for 'terrible' = {}".format(pos_neg_ratios["terrible"]))
Pos-to-neg ratio for 'the' = 0.05902269426102881
Pos-to-neg ratio for 'amazing' = 1.3919815802404802
Pos-to-neg ratio for 'terrible' = -1.7291085042663878

If everything worked, now you should see neutral words with values close to zero. In this case, "the" is near zero but slightly positive, so it was probably used in more positive reviews than negative reviews. But look at "amazing"'s ratio - it's above 1, showing it is clearly a word with positive sentiment. And "terrible" has a similar score, but in the opposite direction, so it's below -1. It's now clear that both of these words are associated with specific, opposing sentiments.

Run the below code to see more ratios.

It displays all the words, ordered by how associated they are with postive reviews.

In [ ]:
pos_neg_ratios.most_common()

The top most common words for the above code : ('edie', 4.6913478822291435), ('paulie', 4.0775374439057197), ('felix', 3.1527360223636558), ('polanski', 2.8233610476132043), ('matthau', 2.8067217286092401), ('victoria', 2.6810215287142909), ('mildred', 2.6026896854443837), ('gandhi', 2.5389738710582761), ('flawless', 2.451005098112319), ('superbly', 2.2600254785752498), ('perfection', 2.1594842493533721), ('astaire', 2.1400661634962708), ('captures', 2.0386195471595809), ('voight', 2.0301704926730531), ('wonderfully', 2.0218960560332353), ('powell', 1.9783454248084671), ('brosnan', 1.9547990964725592)

Transforming Text into Numbers

Creating the Input/Output Data

Create a set named vocab that contains every word in the vocabulary.

In [19]:
vocab = set(total_counts.keys())

Check vocabulary size

In [20]:
vocab_size = len(vocab)
print(vocab_size)
74074

Th following image rpresents the layers of the neural network you'll be building throughout this notebook. layer_0 is the input layer, layer_1 is a hidden layer, and layer_2 is the output layer.

In [1]:
 
Out[1]:

TODO: Create a numpy array called layer_0 and initialize it to all zeros. Create layer_0 as a 2-dimensional matrix with 1 row and vocab_size columns.

In [21]:
layer_0 = np.zeros((1,vocab_size))

layer_0 contains one entry for every word in the vocabulary, as shown in the above image. We need to make sure we know the index of each word, so run the following cell to create a lookup table that stores the index of every word.

TODO: Complete the implementation of update_input_layer. It should count how many times each word is used in the given review, and then store those counts at the appropriate indices inside layer_0.

In [ ]:
# Create a dictionary of words in the vocabulary mapped to index positions 
# (to be used in layer_0)
word2index = {}
for i,word in enumerate(vocab):
    word2index[word] = i

It stores the indexes like this: 'antony': 22, 'pinjar': 23, 'helsig': 24, 'dances': 25, 'good': 26, 'willard': 71500, 'faridany': 27, 'foment': 28, 'matts': 12313,

Lets implement some functions for simplifying our inputs to the neural network.

In [25]:
def update_input_layer(review):
    """
    The element at a given index of layer_0 should represent
    how many times the given word occurs in the review.
    """
     
    global layer_0
    
    # clear out previous state, reset the layer to be all 0s
    layer_0 *= 0
    
    # count how many times each word is used in the given review and store the results in layer_0 
    for word in review.split(" "):
        layer_0[0][word2index[word]] += 1

Run the following cell to test updating the input layer with the first review. The indices assigned may not be the same as in the solution, but hopefully you'll see some non-zero values in layer_0.

In [26]:
update_input_layer(reviews[0])
layer_0
Out[26]:
array([[ 18.,   0.,   0., ...,   0.,   0.,   0.]])

get_target_for_labels should return 0 or 1, depending on whether the given label is NEGATIVE or POSITIVE, respectively.

In [27]:
def get_target_for_label(label):
    if(label == 'POSITIVE'):
        return 1
    else:
        return 0

Building a Neural Network

In [32]:
import time
import sys
import numpy as np

# Encapsulate our neural network in a class
class SentimentNetwork:
    def __init__(self, reviews,labels,hidden_nodes = 10, learning_rate = 0.1):
        """
        Args:
            reviews(list) - List of reviews used for training
            labels(list) - List of POSITIVE/NEGATIVE labels
            hidden_nodes(int) - Number of nodes to create in the hidden layer
            learning_rate(float) - Learning rate to use while training
        
        """
        # Assign a seed to our random number generator to ensure we get
        # reproducable results
        np.random.seed(1)

        # process the reviews and their associated labels so that everything
        # is ready for training
        self.pre_process_data(reviews, labels)
        
        # Build the network to have the number of hidden nodes and the learning rate that
        # were passed into this initializer. Make the same number of input nodes as
        # there are vocabulary words and create a single output node.
        self.init_network(len(self.review_vocab),hidden_nodes, 1, learning_rate)

    def pre_process_data(self, reviews, labels):
        
        # populate review_vocab with all of the words in the given reviews
        review_vocab = set()
        for review in reviews:
            for word in review.split(" "):
                review_vocab.add(word)

        # Convert the vocabulary set to a list so we can access words via indices
        self.review_vocab = list(review_vocab)
        
        # populate label_vocab with all of the words in the given labels.
        label_vocab = set()
        for label in labels:
            label_vocab.add(label)
        
        # Convert the label vocabulary set to a list so we can access labels via indices
        self.label_vocab = list(label_vocab)
        
        # Store the sizes of the review and label vocabularies.
        self.review_vocab_size = len(self.review_vocab)
        self.label_vocab_size = len(self.label_vocab)
        
        # Create a dictionary of words in the vocabulary mapped to index positions
        self.word2index = {}
        for i, word in enumerate(self.review_vocab):
            self.word2index[word] = i
        
        # Create a dictionary of labels mapped to index positions
        self.label2index = {}
        for i, label in enumerate(self.label_vocab):
            self.label2index[label] = i
        
    def init_network(self, input_nodes, hidden_nodes, output_nodes, learning_rate):
        # Set number of nodes in input, hidden and output layers.
        self.input_nodes = input_nodes
        self.hidden_nodes = hidden_nodes
        self.output_nodes = output_nodes

        # Store the learning rate
        self.learning_rate = learning_rate

        # Initialize weights

        # These are the weights between the input layer and the hidden layer.
        self.weights_0_1 = np.zeros((self.input_nodes,self.hidden_nodes))
    
        # These are the weights between the hidden layer and the output layer.
        self.weights_1_2 = np.random.normal(0.0, self.output_nodes**-0.5, 
                                                (self.hidden_nodes, self.output_nodes))
        
        # The input layer, a two-dimensional matrix with shape 1 x input_nodes
        self.layer_0 = np.zeros((1,input_nodes))
    
    def update_input_layer(self,review):

        # clear out previous state, reset the layer to be all 0s
        self.layer_0 *= 0
        
        for word in review.split(" "):
            if(word in self.word2index.keys()):
                self.layer_0[0][self.word2index[word]] += 1
                
    def get_target_for_label(self,label):
        if(label == 'POSITIVE'):
            return 1
        else:
            return 0
        
    def sigmoid(self,x):
        return 1 / (1 + np.exp(-x))
    
    def sigmoid_output_2_derivative(self,output):
        return output * (1 - output)
    
    def train(self, training_reviews, training_labels):
        
        # make sure out we have a matching number of reviews and labels
        assert(len(training_reviews) == len(training_labels))
        
        # Keep track of correct predictions to display accuracy during training 
        correct_so_far = 0

        # Remember when we started for printing time statistics
        start = time.time()
        
        # loop through all the given reviews and run a forward and backward pass,
        # updating weights for every item
        for i in range(len(training_reviews)):
            
            # Get the next review and its correct label
            review = training_reviews[i]
            label = training_labels[i]
            
            ### Forward pass ###

            # Input Layer
            self.update_input_layer(review)

            # Hidden layer
            layer_1 = self.layer_0.dot(self.weights_0_1)

            # Output layer
            layer_2 = self.sigmoid(layer_1.dot(self.weights_1_2))
            
            ### Backward pass ###

            # Output error
            layer_2_error = layer_2 - self.get_target_for_label(label) # Output layer error is the difference between desired target and actual output.
            layer_2_delta = layer_2_error * self.sigmoid_output_2_derivative(layer_2)

            # Backpropagated error
            layer_1_error = layer_2_delta.dot(self.weights_1_2.T) # errors propagated to the hidden layer
            layer_1_delta = layer_1_error # hidden layer gradients - no nonlinearity so it's the same as the error

            # Update the weights
            self.weights_1_2 -= layer_1.T.dot(layer_2_delta) * self.learning_rate # update hidden-to-output weights with gradient descent step
            self.weights_0_1 -= self.layer_0.T.dot(layer_1_delta) * self.learning_rate # update input-to-hidden weights with gradient descent step

            # Keep track of correct predictions.
            if(layer_2 >= 0.5 and label == 'POSITIVE'):
                correct_so_far += 1
            elif(layer_2 < 0.5 and label == 'NEGATIVE'):
                correct_so_far += 1
            
            sys.stdout.write(" #Correct:" + str(correct_so_far) + " #Trained:" + str(i+1) \
                             + " Training Accuracy:" + str(correct_so_far * 100 / float(i+1))[:4] + "%")
    
    def test(self, testing_reviews, testing_labels):
        """
        Attempts to predict the labels for the given testing_reviews,
        and uses the test_labels to calculate the accuracy of those predictions.
        """
        
        # keep track of how many correct predictions we make
        correct = 0

        # Loop through each of the given reviews and call run to predict
        # its label. 
        for i in range(len(testing_reviews)):
            pred = self.run(testing_reviews[i])
            if(pred == testing_labels[i]):
                correct += 1
            
            sys.stdout.write(" #Correct:" + str(correct) + " #Tested:" + str(i+1) \
                             + " Testing Accuracy:" + str(correct * 100 / float(i+1))[:4] + "%")
    
    def run(self, review):
        """
        Returns a POSITIVE or NEGATIVE prediction for the given review.
        """
        # Run a forward pass through the network, like in the "train" function.
        
        # Input Layer
        self.update_input_layer(review.lower())

        # Hidden layer
        layer_1 = self.layer_0.dot(self.weights_0_1)

        # Output layer
        layer_2 = self.sigmoid(layer_1.dot(self.weights_1_2))
        
        # Return POSITIVE for values above greater-than-or-equal-to 0.5 in the output layer;
        # return NEGATIVE for other values
        if(layer_2[0] >= 0.5):
            return "POSITIVE"
        else:
            return "NEGATIVE"
        

Run the following code to create the network with a small learning rate, 0.001, and then train the new network. Using learning rate larger than this, for example 0.1 or even 0.01 would result in poor performance.

In [ ]:
mlp = SentimentNetwork(reviews[:-1000],labels[:-1000], learning_rate=0.001)
mlp.train(reviews[:-1000],labels[:-1000])

Running the above code would have given an accuracy around 62.2%

Reducing Noise in Our Input Data

Counting how many times each word occured in our review might not be the most efficient way. Instead just including whether a word was there or not will improve our training time and accuracy. Hence we update our update_input_layer() function.

In [ ]:
def update_input_layer(self,review):
    self.layer_0 *= 0
        
    for word in review.split(" "):
        if(word in self.word2index.keys()):
            self.layer_0[0][self.word2index[word]] =1

Creating and running our neural network again, even with a higher learning rate of 0.1 gave us a training accuracy of 83.8% and testing accuracy(testing on last 1000 reviews) of 85.7%.

Reducing Noise by Strategically Reducing the Vocabulary

Let us put the pos to neg ratio's that we found were much more effective at detecting a positive or negative label. We could do that by a few change:

  • Modify pre_process_data:
    • Add two additional parameters: min_count and polarity_cutoff
    • Calculate the positive-to-negative ratios of words used in the reviews.
    • Change so words are only added to the vocabulary if they occur in the vocabulary more than min_count times.
    • Change so words are only added to the vocabulary if the absolute value of their postive-to-negative ratio is at least polarity_cutoff
In [ ]:
def pre_process_data(self, reviews, labels, polarity_cutoff, min_count):
        
        positive_counts = Counter()
        negative_counts = Counter()
        total_counts = Counter()

        for i in range(len(reviews)):
            if(labels[i] == 'POSITIVE'):
                for word in reviews[i].split(" "):
                    positive_counts[word] += 1
                    total_counts[word] += 1
            else:
                for word in reviews[i].split(" "):
                    negative_counts[word] += 1
                    total_counts[word] += 1

        pos_neg_ratios = Counter()

        for term,cnt in list(total_counts.most_common()):
            if(cnt >= 50):
                pos_neg_ratio = positive_counts[term] / float(negative_counts[term]+1)
                pos_neg_ratios[term] = pos_neg_ratio

        for word,ratio in pos_neg_ratios.most_common():
            if(ratio > 1):
                pos_neg_ratios[word] = np.log(ratio)
            else:
                pos_neg_ratios[word] = -np.log((1 / (ratio + 0.01)))

        # populate review_vocab with all of the words in the given reviews
        review_vocab = set()
        for review in reviews:
            for word in review.split(" "):
                if(total_counts[word] > min_count):
                    if(word in pos_neg_ratios.keys()):
                        if((pos_neg_ratios[word] >= polarity_cutoff) or (pos_neg_ratios[word] <= -polarity_cutoff)):
                            review_vocab.add(word)
                    else:
                        review_vocab.add(word)

        # Convert the vocabulary set to a list so we can access words via indices
        self.review_vocab = list(review_vocab)
        
        # populate label_vocab with all of the words in the given labels.
        label_vocab = set()
        for label in labels:
            label_vocab.add(label)
        
        # Convert the label vocabulary set to a list so we can access labels via indices
        self.label_vocab = list(label_vocab)
        
        # Store the sizes of the review and label vocabularies.
        self.review_vocab_size = len(self.review_vocab)
        self.label_vocab_size = len(self.label_vocab)
        
        # Create a dictionary of words in the vocabulary mapped to index positions
        self.word2index = {}
        for i, word in enumerate(self.review_vocab):
            self.word2index[word] = i
        
        # Create a dictionary of labels mapped to index positions
        self.label2index = {}
        for i, label in enumerate(self.label_vocab):
            self.label2index[label] = i

Our training accuracy increased to 85.6% after this change. As we can see our accuracy saw a huge jump by making minor changes based on our intuition. We can keep making such changes and increase the accuracy even further.

 

Download the Data Sources

The data sources used in this article can be downloaded here:

R oder Python – Die Sprache der Wahl in einem Data Science Weiterbildungskurs

Die KDnuggets, ein einflussreicher Newletter zu Data Mining und inzwischen auch zu Data Science, überraschte kürzlich mit der Meldung „Python eats away at R: Top Software for Analytics, Data Science, Machine Learning in 2018. Trends and Analysis“.[1] Grundlage war eine Befragung, an der mehr als 2300 KDNuggets Leser teilnahmen. Nach Bereinigung um die sogenannten „Lone Voters“, gingen insgesamt 2052 Stimmen in die Auswertung ein.

Demnach stieg der Anteil der Python-Nutzer von 2017 bis 2018 um 11% auf 65%, während mit 48% weniger als die Hälfte der Befragungsteilnehmer noch R nannten. Gegenüber 2017 ging der Anteil von R um 14% zurück. Dies ist umso bemerkenswerter, als dass bei keinem der übrigen Top Tools eine Verminderung des Anteils gemessen wurde.

Wir verzichten an dieser Stelle darauf, die Befragungsergebnisse selbst in Frage zu stellen oder andere Daten herbeizuziehen. Stattdessen nehmen wir erst einmal die Zahlen wie sie sind und konzedieren einen gewissen Python Hype. Das Python Konjunktur hat, zeigt sich z.B. in der wachsenden Zahl von Buchtiteln zu Python und Data Science oder in einem Machine Learning Tutorial der Zeitschrift iX, das ebenfalls auf Python fußt. Damit stellt sich die Frage, ob ein Weiterbildungskurs zu Data Science noch guten Gewissens auf R als Erstsprache setzen kann.

Der Beantwortung dieser Frage seien zwei Bemerkungen vorangestellt:

  1. Ob die eine Sprache „besser“ als die andere ist, lässt sich nicht abschließend beantworten. Mit Blick auf die Teilarbeitsgebiete des Data Scientists, also Datenzugriff, Datenmanipulation und Transformation, statistische Analysen und visuelle Aufbereitung zeigt sich jedenfalls keine prinzipielle Überlegenheit der einen über die andere Sprache.
  2. Beide Sprachen sind quicklebendig und werden bei insgesamt steigenden Nutzerzahlen dynamisch weiterentwickelt.

Das Beispiel der kürzlich gegründeten Ursa Labs[2] zeigt überdies, dass es zukünftig weniger darum gehen wird „Werkzeuge für eine einzelne Sprache zu bauen…“ als darum „…portable Bibliotheken zu entwickeln, die in vielen Programmiersprachen verwendet werden können“[3].

Die zunehmende Anwendung von Python in den Bereichen Data Science und Machine Learning hängt auch damit zusammen, dass Python ursprünglich als Allzweck-Programmiersprache konzipiert wurde. Viele Entwickler und Ingenieure arbeiteten also bereits mit Python ohne dabei mit analytischen Anwendungen in Kontakt zu kommen. Wenn diese Gruppen gegenwärtig mehr und mehr in den Bereichen Datenanalyse, Statistik und Machine Learning aktiv werden, dann greifen sie naturgemäß zu einem bekannten Werkzeug, in diesem Fall zu einer bereits vorhandenen Python Implementation.

Auf der anderen Seite sind Marketingfachleute, Psychologen, Controller und andere Analytiker eher mit SPSS und Excel vertraut. In diesen Fällen kann die Wahl der Data Science Sprache freier erfolgen. Für R spricht dann zunächst einmal seine Kompaktheit. Obwohl inzwischen mehr als 10.000 Erweiterungspakete existieren, gibt es mit www.r-project.org immer noch eine zentrale Anlaufstelle, von der über einen einzigen Link der Download eines monolithischen Basispakets erreichbar ist.

Demgegenüber existieren für Python mit Python 2.7 und Python 3.x zwei nach wie vor aktive Entwicklungszweige. Fällt die Wahl z.B. auf Python 3.x, dann stehen mit Python3 und Ipython3 wiederum verschiedene Interpreter zur Auswahl. Schließlich gibt es noch Python Distributionen wie Anaconda. Anaconda selbst ist in zwei „Geschmacksrichtungen“ (flavors) verfügbar als Miniconda und eben als Anaconda.

R war von Anfang an als statistische Programmiersprache konzipiert. Nach allen subjektiven Erfahrungen eignet es sich allein schon deshalb besser zur Erläuterung statistischer Methoden. Noch vor wenigen Jahren galt R als „schwierig“ und Statistikern vorbehalten. In dem Maße, in dem wissenschaftlich fundierte Software Tools in den Geschäftsalltag vordringen wird klar, dass viele der zunächst als „schwierig“ empfundenen Konzepte letztlich auf Rationalität und Arbeitsersparnis abzielen. Fehler, Bugs und Widersprüche finden sich in R so selbstverständlich wie in allen anderen Programmiersprachen. Bei der raschen Beseitigung dieser Schwächen kann R aber auf eine große und wache Gemeinschaft zurückgreifen.

Die Popularisierung von R erhielt durch die Gründung des R Consortiums zu Beginn des Jahres 2015 einen deutlichen Schub. Zu den Initiatoren dieser Interessengruppe gehörte auch Microsoft. Tatsächlich unterstützt Microsoft R auf vielfältige Weise unter anderem durch eine eigene Distribution unter der Bezeichnung „Microsoft R Open“, die Möglichkeit R Code in SQL Anweisungen des SQL Servers absetzen zu können oder die (angekündigte) Weitergabe von in Power BI erzeugten R Visualisierungen an Excel.

Der Vergleich von R und Python in einem fiktiven Big Data Anwendungsszenario liefert kein Kriterium für die Auswahl der Unterrichtssprache in einem Weiterbildungskurs. Aussagen wie x ist „schneller“, „performanter“ oder „besser“ als y sind nahezu inhaltsleer. In der Praxis werden geschäftskritische Big Data Anwendungen in einem Umfeld mit vielen unterschiedlichen Softwaresystemen abgewickelt und daher von vielen Parametern beeinflusst. Wo es um Höchstleistungen geht, tragen R und Python häufig gemeinsam zum Ergebnis bei.

Der Zertifikatskurs „Data Science“ der AWW e. V. und der Technischen Hochschule Brandenburg war schon bisher nicht auf R beschränkt. Im ersten Modul geben wir z.B. auch eine Einführung in SQL und arbeiten mit ETL-Tools. Im gerade zu Ende gegangenen Kurs wurde Feature Engineering auf der Grundlage eines Python Lehrbuchs[4] behandelt und die Anweisungen in R übersetzt. In den kommenden Durchgängen werden wir dieses parallele Vorgehen verstärken und wann immer sinnvoll auch auf Lösungen in Python hinweisen.

Im Vertiefungsmodul „Machine Learning mit Python“ schließlich ist Python die Sprache der Wahl. Damit tragen wir der Tatsache Rechnung, dass es zwar Sinn macht in die grundlegenden Konzepte mit einer Sprache einzuführen, in der Praxis aber Mehrsprachigkeit anzutreffen ist.

[1] https://www.kdnuggets.com/2018/05/poll-tools-analytics-data-science-machine-learning-results.html

[2] https://ursalabs.org/

[3] Statement auf der Ursa Labs Startseite, eigene Übersetzung.

[4] Sarkar, D et al. Practical Machine Learning with Python, S. 177ff.

Bringing intelligence to where data lives: Python & R embedded in T-SQL

Introduction

Did you know that you can write R and Python code within your T-SQL statements? Machine Learning Services in SQL Server eliminates the need for data movement. Instead of transferring large and sensitive data over the network or losing accuracy with sample csv files, you can have your R/Python code execute within your database. Easily deploy your R/Python code with SQL stored procedures making them accessible in your ETL processes or to any application. Train and store machine learning models in your database bringing intelligence to where your data lives.

You can install and run any of the latest open source R/Python packages to build Deep Learning and AI applications on large amounts of data in SQL Server. We also offer leading edge, high-performance algorithms in Microsoft’s RevoScaleR and RevoScalePy APIs. Using these with the latest innovations in the open source world allows you to bring unparalleled selection, performance, and scale to your applications.

If you are excited to try out SQL Server Machine Learning Services, check out the hands on tutorial below. If you do not have Machine Learning Services installed in SQL Server,you will first want to follow the getting started tutorial I published here: 

How-To Tutorial

In this tutorial, I will cover the basics of how to Execute R and Python in T-SQL statements. If you prefer learning through videos, I also published the tutorial on YouTube.

Basics

Open up SQL Server Management Studio and make a connection to your server. Open a new query and paste this basic example: (While I use Python in these samples, you can do everything with R as well)

Sp_execute_external_script is a special system stored procedure that enables R and Python execution in SQL Server. There is a “language” parameter that allows us to choose between Python and R. There is a “script” parameter where we can paste R or Python code. If you do not see an output print 7, go back and review the setup steps in this article.

Parameter Introduction

Now that we discussed a basic example, let’s start adding more pieces:

Machine Learning Services provides more natural communications between SQL and R/Python with an input data parameter that accepts any SQL query. The input parameter name is called “input_data_1”.
You can see in the python code that there are default variables defined to pass data between Python and SQL. The default variable names are “OutputDataSet” and “InputDataSet” You can change these default names like this example:

As you executed these examples, you might have noticed that they each return a result with “(No column name)”? You can specify a name for the columns that are returned by adding the WITH RESULT SETS clause to the end of the statement which is a comma separated list of columns and their datatypes.

Input/Output Data Types

Alright, let’s discuss a little more about the input/output data types used between SQL and Python. Your input SQL SELECT statement passes a “Dataframe” to python relying on the Python Pandas package. Your output from Python back to SQL also needs to be in a Pandas Dataframe object. If you need to convert scalar values into a dataframe here is an example:

Variables c and d are both scalar values, which you can add to a pandas Series if you like, and then convert them to a pandas dataframe. This one shows a little bit more complicated example, go read up on the python pandas package documentation for more details and examples:

You now know the basics to execute Python in T-SQL!

Did you know you can also write your R and Python code in your favorite IDE like RStudio and Jupyter Notebooks and then remotely send the execution of that code to SQL Server? Check out these documentation links to learn more: https://aka.ms/R-RemoteSQLExecution https://aka.ms/PythonRemoteSQLExecution

Check out the SQL Server Machine Learning Services documentation page for more documentation, samples, and solutions. Check out these E2E tutorials on github as well.

Would love to hear from you! Leave a comment below to ask a question, or start a discussion!

Distributed Computing – MapReduce Algorithmus

Sollen große Datenmengen analysiert werden, ist die Hardware eines leistungsfähigen Computers schnell überfordert und die Analysezeiten werden zu lang. Die Lösung zur Bewältigung von Big Data Analytics sind Konzepte des verteilten Rechnens (Distributed Computing).

Vertikale Skalierung – Der Klassiker der leistungsstarken Datenverarbeitung

Die meisten Unternehmen setzen heute noch auf leistungsstarke und aufrüstbare Einzelserver. Sollten Datenmengen größer und Analysen rechenaufwändiger werden, werden Festplatten (Storage), Arbeitsspeicher (RAM) und Prozessoren (CPU) aufgerüstet oder der Server direkt durch einen leistungsstärkeren ersetzt.

Diese Form der sogenannten vertikalen Skalierung (Vergrößerung der Server-Komponenten) ist für viele Unternehmen heute noch gängige Praxis, auch weil sie leicht zu administrieren ist und sie mit nahezu jeder Software funktioniert. Jedoch sind der Erweiterbarkeit gewisse Grenzen gesetzt und auch der Wechsel zu noch leistungsfähigerer Hardware würde den Einsatz von neuester High-End-Hardware bedeuten, der Kostenanstieg wäre exponentiell. Ferner bedarf es einer durchdachten Backup-Strategie mit gespiegelten Festplatten oder einem ganzen Backup-Server.

Leistungsstarke Server sind teuer und können zwar große Datenmengen weitaus schneller auswerten als Consumer-Computer, jedoch sind auch sie eher nicht dazu in der Lage, Big Data zu verarbeiten, also beispielsweise 100 Terabyte Daten binnen Sekunden statistisch auszuwerten.

Horizontale Skalierung – Skalierbare Speicher-/Rechenleistung

Ein alternatives Konzept zur vertikalen Skalierung ist die horizontale Skalierung. Dabei werden mehrere Computer, die im Vergleich oftmals über nur mittelmäßige Leistungsmerkmale verfügen, über ein Computer-Netzwerk verbunden und parallel angesteuert.

Der große Vorteil der horizontalen Skalierung ist der kostengünstige Einstieg, denn praktisch könnte bereits mit einem einzelnen Computer (Node) begonnen werden und dann nach und nach mit weiteren Nodes die Leistungsfähigkeit des Clusters (Verbund von Nodes) linear gesteigert werden. Ungefähr linear wachsen auch die Kosten an, so dass diese weitaus besser planbar sind. Cluster können weitaus höhere Leistungen erreichen als es einzelne Server könnten, daher gibt die horizontale Skalierung als diejenige, die sich für Big Data Analytics eignet, denn sie ermöglicht verteiltes Rechnen (Distributed Computing). Mit einem ausreichend großen Cluster lassen sich auch 100 Terabyte und mehr in wenigen Augenblicken statistisch auswerten.

Ferner ermöglichen horizontale Lösungen integrierte Backup-Strategien, indem jeder Node des Clusters über ein Backup der Daten eines anderen Nodes verfügt. Verfügt ein Node sogar über mehrere Backups, lässt sich eine sehr hohe Ausfallsicherheit – Datenverfügbarkeit im Cluster – erzielen.

Jedoch gibt es auch Nachteile der horizontalen Skalierung: Die Administration eines Clusters ist weitaus herausfordernder als ein einzelner Server, egal wie leistungsstark dieser sein mag. Auch Bedarf es viel räumlichen Platz für einen (oder gar mehrere) Cluster. Die Kompatibilität der Nodes sollte auch für die nächsten Jahr gesichert sein und nicht zuletzt ist es eine große Hürde, dass die einzusetzende Software (Datenbank- und Analyse-Software) für den Einsatz auf Clustern geeignet sein muss. Verbreite Software-Lösungen für verteiltes Speichern und Rechnen kommen beispielsweise von der Apache Foundation als Open Source Software: Hadoop, Spark und Flink.

Map Reduce Processing

Damit verteiltes Rechnung funktioniert, bedarf es der richtigen Software, die wiederum Algorithmen einsetzt, die sich dafür eignen. Der bekannteste und immer noch am weitesten verbreitete Algorithmus ist MapReduce. MapReduce ist ein sehr einfacher Algorithmus und dürfte von der grundsätzlichen Vorgehensweise jedem Software-Entwickler oder Analyst vertraut sein. Das Prinzip entspricht dem folgenden SQL-Statement, dass die am häufigsten vorkommende Sprache aus dem Datensatz (Tabelle Customers) abfragt:

Es gibt eine Tabelle (es könnte eine Tabelle in einer relationalen Datenbank sein oder eine CSV-Datei), die durch eine SELECT-Query abgefragt (map), groupiert (combine) und sortiert (sort). Dieser Schritt kann vereinfacht als Map-Funktion betrachtet werden, die in einer Liste Paaren aus Schlüssel (Keys) und Werten (Values) resultiert. Ist diese Liste vorhanden, kann diese auf die gewünschten Ergebnisse entspechend einer Logik (z. B. max(), min(), mean(), sum()) auf wenige oder nur einen einzigen Wert reduziert werden (Reduce-Funktion). Zu beachten ist dabei, dass der Map-Prozess sehr viel speicher- und rechen-aufwändiger als der Reduce-Prozess ist. Führen wir diese Abfrage auf einer Maschine aus, fassen wir die beiden Abfragen als ein Statement aus:

Betrachten wir jedoch die einzelnen Schritte, können wir sie wieder zumindest in einen Map- und einen Reduce-Schritt unterteilen. Diese Aufteilung machen wir uns für das verteilte Rechnen zunutze: Wenn jeder Computer (Node; oft auch Client Node oder Data Node) einen Teil der Daten besitzt, kann jeder Node für sich einen Map-Prozess durchführen, die Ergebnisse dann an einen Master-Node (oder in Hadoop-Sprache: Name Node) senden, der den Reduce-Prozess durchführt. Der Großteil der Aufgabe findet somit auf dem Cluster statt, nur der simple Reduce-Schritt auf einem einzelnen Computer.

Oftmals reicht ein parallel ablaufender Map-Prozess auf dem Cluster nicht aus, um Daten effizient auswerten zu können. Die Maßgabe sollte stets sein, den Reduce-Aufwand so gering wie möglich zu halten und soviel Arbeit wie möglich auf den Cluster zu verlagern. Daher sollte jeder Node im Cluster soweit aggregieren wie möglich: Dafür gibt es den Combine-Schritt.

Die zuvor erwähnte SQL-Abfrage als MapReduce würde bedeuten, dass ein Node über den Datensatz verfügt (und andere Nodes über weitere Datensätze) und jeder Node für sich seine Daten über einen Map-Prozess herausarbeitet, über einen Combine-Prozess aggregiert und die Aggregationsergebnisse an den Master-Node (Name Node) sendet. Hat der Master-Node alle Ergebnisse erhalten, berechnet dieser daraus das Endergebnis (Reduce).

Zusammenfassung: Map Reduce

MapReduce ist der bekannteste Algorithmus zur verteilten Verarbeitung von Daten und eignet sich für die Durchführung von komplexen Datenanalysen. Liegen Datensätze auf mehreren Computern (Client Nodes) vor, läuft der Algorithmus in der Regel in drei Schritten ab:

  1. Map – Selektierung der Datensätze auf den Computern im gewünschten Format und Durchführung einer Berechnung, beispielsweise der Bildung einer Summe. Dieser Schritt ist ermöglich das Prinzip Schema on Read, das aus Hadoop ein Tool zur Verarbeitung von unstrukturierten Daten macht.
  2. Combine – Durchführung einer Aggregation, die ebenfalls auf jeden Client Node durchgeführt wird, zur Zusammenfassung von Map-Ergebnissen.
  3. Reduce – Aggregation aller Ergebnisse auf dem zentralen Rechner (Name Node)

MapReduce ist dazu geeignet, unstrukturierte Daten zu verarbeiten, denn das Format der Daten wird über einen Map-Algorithmus bestimmt, der sehr flexibel programmiert werden kann. MapReduce ist kein eng definierter Algorithmus, sondern eine Hülle, die mit Inhalt befüllt werden muss. So müssen MapReduce-Algorithmen individuell über eine Programmiersprache wie Java, Scala oder Python programmiert werden.

Ein Beispiel eines in Java programmierten Word-Count-Algorithmus nach der MapReduce-Logik in Hadoop findet sich hier:

MapReduce und Advanced Analytics

MapReduce spielt seine Vorteile auf Computer-Clustern aus und eignet sich sehr zur Analyse von Daten nach dem Schema on Read. Für kompliziertere Analysealgorithmen ist MapReduce jedoch nur bedingt geeignet, denn bereits einfache Join-Anweisungen benötigen mehrere MapReduce-Ketten.

Während statistische Auswertungen und Join-Anweisungen mit MapReduce noch gut möglich sind, werden Algorithmen des maschinellen Lernens schwierig bis kaum möglich, da diese viele Iterationen, z. B. zur Anpassung von Gewichten, benötigen.

Self Service Data Preparation mit Microsoft Excel

Get & Transform (vormals Power Query), eine kurze Einführung

 Unter Data Preparation versteht man sinngemäß einen Prozeß der Vorbereitung / Aufbereitung von Rohdaten aus meistens unterschiedlichen Datenquellen und -formaten, verbunden mit dem Ziel, diese effektiv für verschiedene Geschäftszwecke / Analysen (Business Fragen) weiterverwenden/bereitstellen zu können. Rohdaten müssen oft vor ihrem bestimmungsgemäßen Gebrauch transformiert (Datentypen), integriert (Datenkonsistenz, referentielle Integrität), sowie zugeordnet (mapping; Quell- zu Zieldaten) werden.
An diesem neuralgischen Punkt werden bereits die Weichen für Datenqualität gestellt.

Unter Datenqualität soll hier die Beschaffenheit / Geeignetheit von Daten verstanden werden, um konkrete Fragestestellungen beantworten zu können (fitness for use):

Kriterien Datenqualität

  • Eindeutigkeit
  • Vollständigkeit
  • Widerspruchsfreiheit / Konsistenz
  • Aktualität
  • Genauigkeit
  • Verfügbarkeit

Datenqualität bestimmt im Wesentlichen die weitere zielgerichtete Verwendung der Daten in Analysen (Modelle) und Berichten (Reporting). Daten werden in entscheidungsrelevante Kennzahlen (Informationen) überführt. Eine Kennzahl ist gegenüber der Datenqualität immer blind, ihre Aussagekraft (Validität) hängt -neben der Definition – in sehr starkem Maße davon ab:

Gütekriterien von Kennzahlen

  • Objektivität := ist die Interpretation unabhängig vom Beobachter / Verwender?
  • Reliabilität := kann das Ergebnis unter sonst gleichen Bedingungen reproduziert werden ?
  • Validität := sagt die Kennzahl das aus, was sie vorgibt, auszusagen ?

Business Fragen entstehen naturgemäß in den Fachbereichen.Daher ist es nur folgerichtig, Data Preparation als einen ersten Analyseschritt innerhalb des Fachbereichs anzusiedeln (Self Service Data Preparation). Dadurch erhält der Fachbereich einen Teil seiner Autonomie zurück. Welche Teilmenge der Daten relevant für Fragestellungen ist, kann nur der Fachbereich beurteilen; der Anforderer von entscheidungsrelevanten Informationen sollte idealerweiseTeil der Entstehung wertiger Daten sein, das fördert zum einen die Akzeptanz des Ergebnisses, zum anderen wirkt es einem „not-invented-here“ Syndrom frühzeitig entgegen.

Im Folgenden wird anhand 4 Schritten skizziert, wie Microsoft Excel bei dem Thema (Self Service) Data Preparation vor allem den Fachbereich unterstützen kann. Eine Beispieldatei können Sie hier (google drive) einsehen. Sie finden die hierfür verwendete Funktionalität (Get & Transform) in Excel 2016 unter:

Reiter Daten -> Abrufen und Transformieren.

Dem interessierten Leser werden im Text vertiefende Informationen über links zu einzelnen typischen Aufgabenstellungen und Lösungswegen angeboten. Eine kurze Einführung in das Thema finden Sie in diesem Blog Beitrag.

1 Einlesen

Datenquellen anbinden (externe, interne)

Dank der neuen Funktionsgruppe „Abrufen und Transformieren“ ist es in Microsoft Excel möglich, verschiedene externe Datenquellen /-formate anzubinden. Zusätzlich können natürlich auch Tabellen der aktiven / offenen Excel Arbeitsmappe als Datenquelle dienen (interne Datenquellen). Diese Datenquellen werden anschließend als sogenannte Arbeitsmappenabfragen abgebildet.

Praxisbeispiele:

Anbindung mehrerer Dateien, welche in einem Ordner bereitgestellt werden

Anbindung von Webinhalten

2 Transformieren

Daten transformieren (Datentypen, Struktur)

Datentypen (Text, Zahl) können anschließend je Arbeitsmappenabfrage und Spalte(n) geändert werden.
Dies ist zB immer dann notwendig, wenn Abfragen über Schlüsselspalten in Beziehung gesetzt werden sollen (siehe Punkt 3). Gleicher Datentyp (Primär- und Fremdschlüssel) in beiden Tabellen ist hier notwendige Voraussetzung.

Des Weiteren wird in dieser Phase typischerweise festgelegt, welche Zeile der Abfrage die Spaltenbeschriftungen enthält.

Praxisbeispiele:

Fehlerbehandlung

Leere Zellen auffüllen

Umgang mit wechselnden Spaltenbeschriftungen

3 Zusammenführen / Anreichern

Daten zusammenführen (SVERWEIS mal anders)

Um unterschiedliche Tabellen / Abfragen über gemeinsame Schlüsselspalten zusammenzuführen, stellt der Excel Abfrage Editor eine Reihe von JOIN-Operatoren zur Verfügung, welche ohne SQL-Kenntnisse nur durch Anklicken ausgewählt werden können.

Praxisbeispiele

JOIN als Alternative zu Excel Formel SVERWEIS()

Daten anreichern (benutzerdefinierte Spalte anfügen)

Bei Bedarf können weitere Daten, welche sich nicht in der originären Struktur der Datenquelle befinden, abgeleitet werden. Die Sprache Language M stellt einen umfangreichen Katalog an Funktionen zur Verfügung. Wie Sie eine Übersicht über die verfügbaren Funktionen erhalten können erfahren Sie hier.

Praxisbeispiele

Geschäftsjahr aus Datum ableiten

Extraktion Textteil aus Text (Trunkation)

Mehrfache Fallunterscheidung, Datenbereinigung /-harmonisierung

4 Laden

Daten laden

Die einzelnen Arbeitsmappenabfragen können abschließend in eine Exceltabelle, eine Verbindung und / oder in das Power Pivot Datemodell zur weiteren Bearbeitung (Modellierung, Kennzahlenbildung) geladen werden.

Praxisbeispiele

Datenverbindung erstellen

Show your Data Science Workplace!

The job of a data scientist is often a mystery to outsiders. Of course, you do not really need much more than a medium-sized notebook to use data science methods for finding value in data. Nevertheless, data science workplaces can look so different and, let’s say, interesting. And that’s why I want to launch a blog parade – which I want to start with this article – where you as a Data Scientist or Data Engineer can show your workplace and explain what tools a data scientist in your opinion really needs.

I am very curious how many monitors you prefer, whether you use Apple, Dell, HP or Lenovo, MacOS, Linux or Windows, etc., etc. And of course, do you like a clean or messy desk?

What is a Blog Parade?

A blog parade is a call to blog owners to report on a specific topic. Everyone who participates in the blog parade, write on their blog a contribution to the topic. The organizer of the blog parade collects all the articles and will recap those articles in a short form together, of course with links to the articles.

How can I participate?

Write an article on your blog! Mention this blog parade here, show and explain your workplace (your desk with your technical equipment) in an article. If you’re missing your own blog, articles can also be posted directly to LinkedIn (LinkedIn has its own blogging feature that every LinkedIn member can use). Alternative – as a last resort – it would also be possible to send me your article with a photo about your workplace directly to: redaktion@data-science-blog.com.
Please make me aware of an article, via e-mail or with a comment (below) on this article.

Who can participate?

Any data scientist or anyone close to Data Science: Everyone concerned with topics such as data analytics, data engineering or data security. Please do not over-define data science here, but keep it in a nutshell, so that all professionals who manage and analyze data can join in with a clear conscience.

And yes, I will participate too. I will propably be the first who write an article about my workplace (I just need a new photo of my desk).

When does the article have to be finished?

By 31/12/2017, the article must have been published on your blog (or LinkedIn or wherever) and the release has to be reported to me.
But beware: Anyone who has previously written an article will also be linked earlier. After all, reporting on your article will take place immediately after I hear about it.
If you publish an artcile tomorrow, it will be shown the day after tomorrow here on the Data Science Blog.

What is in it for me to join?

Nothing! Except perhaps the fun factor of sharing your idea of ​​a nice desk for a data expert with others, so as to share creativity or a certain belief in what a data scientist needs.
Well and for bloggers: There is a great backlink from this data science blog for you 🙂

What should I write? What are the minimum requirements of content?

The article does not have to (but may be) particularly long. Anyway, here on this data science blog only a shortened version of your article will appear (with a link, of course).

Minimum requirments:

  • Show a photo (at least one!) of your workplace desk!
  • And tell us something about:
    • How many monitors do you use (or wish to have)?
    • What hardware do you use? Apple? Dell? Lenovo? Others?
    • Which OS do you use (or prefer)? MacOS, Linux, Windows? Virtual Machines?
    • What are your favorite databases, programming languages and tools? (e.g. Python, R, SAS, Postgre, Neo4J,…)
    • Which data dou you analyze on your local hardware? Which in server clusters or clouds?
    • If you use clouds, do you prefer Azure, AWS, Google oder others?
    • Where do you make your notes/memos/sketches. On paper or digital?

Not allowed:
Of course, please do not provide any information, which could endanger your company`s IT security.

Absolutly allowed:
Bringing some joke into the matter 🙂 We are happy to vote in the comments on the best or funniest desk for election, there may be also a winner later!


The resulting Blog Posts: https://data-science-blog.com/data-science-insights/show-your-desk/


 

Big Data Essentials – Intro

1. Big Data Definition

Data umfasst Nummern, Text, Bilder, Audio, Video und jede Art von Informationen die in Ihrem Computer gespeichert werden können. Big Data umfasst Datenmengen, die eine oder mehrere der folgenden Eigenschaften aufweisen: Hohes Volumen (High Volume), hohe Vielfalt (High Variety) und / oder eine notwendige hohe Geschwindigkeit (High Velocity) zur Auswertung. Diese drei Eigenschaften werden oft auch als die 3V’s von Big Data bezeichnet.

1.1. Volumen: Menge der erzeugten Daten

Volumen bezieht sich auf die Menge der generierten Daten. Traditionelle Datenanalysemodelle erfordern typischerweise Server mit großen Speicherkapazitäten, bei massiver Rechenleistung sind diese Modelle nicht gut skalierbar. Um die Rechenleistung zu erhöhen, müssen Sie weiter investieren, möglicherweise auch in teurere proprietäre Hardware. Die NASA ist eines von vielen Unternehmen, die enorme Mengen an Daten sammeln. Ende 2014 sammelte die NASA alle paar Sekunden etwa 1,73 GB an Daten. Und auch dieser Betrag der Datenansammlung steigt an, so dass die Datenerfassung entsprechend exponentiell mitwachsen muss. Es resultieren sehr hohe Datenvolumen und es kann schwierig sein, diese zu speichern.

1.2. Vielfalt: Unterschiedliche Arten von Daten

Das  traditionelle  Datenmodell (ERM)  erfordert  die  Entwicklung  eines  Schemas,  das  die  Daten in ein Korsett zwingt. Um das Schema zu erstellen, muss man das Format der Daten kennen, die gesammelt werden. Daten  können  wie  XML-Dateien  strukturiert  sein,  halb  strukturiert  wie  E-Mails oder unstrukturiert wie Videodateien.

Wikipedia – als Beispiel – enthält mehr als nur Textdaten, es enthält Hyperlinks, Bilder, Sound-Dateien und viele andere Datentypen mit mehreren verschiedenen Arten von Daten. Insbesondere unstrukturierte   Daten haben   eine   große   Vielfalt.  Es   kann   sehr   schwierig   sein, diese Vielfalt in einem Datenmodell zu beschreiben.

1.3. Geschwindigkeit: Geschwindigkeit, mit der Daten genutzt werden

Traditionelle Datenanalysemodelle wurden für die Stapelverarbeitung (batch processing) entwickelt. Sie sammeln die gesamte Datenmenge und verarbeiten sie, um sie in die Datenbank zu speichern. Erst mit einer Echtzeitanalyse der Daten kann schnell auf Informationen reagiert werden. Beispielsweise können Netzwerksensoren, die mit dem Internet der Dinge (IoT) verbunden sind, tausende von Datenpunkten pro Sekunde erzeugen. Im Gegensatz zu Wikipedia, deren Daten später verarbeitet werden können, müssen Daten von Smartphones und anderen Netzwerkteilnehmern mit entsprechender Sensorik in  Echtzeit  verarbeitet  werden.

2. Geschichte von Big Data

2.1. Google Solution

  • Google File System speichert die Daten, Bigtable organisiert die Daten und MapReduce verarbeitet es.
  • Diese Komponenten arbeiten zusammen auf einer Sammlung von Computern, die als Cluster bezeichnet werden.
  • Jeder einzelne Computer in einem Cluster wird als Knoten bezeichnet.

2.2 Google File System

Das Google File System (GFS) teilt Daten in Stücke ‚Chunks’ auf. Diese ‚Chunks’ werden verteilt und auf verschiedene Knoten in einem Cluster nachgebildet. Der Vorteil ist nicht nur die mögliche parallele Verarbeitung bei der späteren Analysen, sondern auch die Datensicherheit. Denn die Verteilung und die Nachbildung schützen vor Datenverlust.

2.3. Bigtable

Bigtable ist ein Datenbanksystem, das GFS zum Speichern und Abrufen von Daten verwendet. Trotz seines Namens ist Bigtable nicht nur eine sehr große Tabelle. Bigtable ordnet die Datenspeicher mit einem Zeilenschlüssel, einem Spaltenschlüssel und einem Zeitstempel zu. Auf diese Weise können dieselben Informationen über einen längeren Zeitraum hinweg erfasst werden, ohne dass bereits vorhandene Einträge überschrieben werden. Die Zeilen sind dann in den Untertabellen partitioniert, die über einem Cluster verteilt sind. Bigtable wurde entwickelt, um riesige Datenmengen zu bewältigen, mit der Möglichkeit, neue Einträge zum Cluster hinzuzufügen, ohne dass eine der vorhandenen Dateien neu konfiguriert werden muss.

2.4. MapReduce

Als dritter Teil des Puzzles wurde ein Parallelverarbeitungsparadigma namens MapReduce genutzt, um die bei GFS gespeicherten Daten zu verarbeiten. Der Name MapReduce wird aus den Namen von zwei Schritten im Prozess übernommen. Obwohl der Mapreduce-Prozess durch Apache Hadoop berühmt geworden ist, ist das kaum eine neue Idee. In der Tat können viele gängige Aufgaben wie Sortieren und Falten von Wäsche als Beispiele für den MapReduce- Prozess betrachtet werden.

Quadratische Funktion:

  • wendet die gleiche Logik auf jeden Wert an, jeweils einen Wert
  • gibt das Ergebnis für jeden Wert aus
    (map square'(1 2 3 4)) = (1 4 9 16)

Additionsfunktion

  • wendet die gleiche Logik auf alle Werte an, die zusammen genommen werden.
    (reduce + ‘(1 4 9 16)) = 30

Die Namen Map und Reduce können bei der Programmierung mindestens bis in die 70er-Jahre zurückverfolgt werden. In diesem Beispiel sieht man, wie die Liste das MapReduce-Modell verwendet. Zuerst benutzt man Map der Quadratfunktion auf einer Eingangsliste für die Quadratfunktion, da sie abgebildet ist, alle angelegten Eingaben und erzeugt eine einzige Ausgabe pro Eingabe, in diesem Fall (1, 4, 9 und 16). Additionsfunktion reduziert die Liste und erzeugt eine einzelne Ausgabe von 30, der die Summe aller Eingänge ist.

Google nutzte die Leistung von MapReduce, um einen Suchmaschinen-Markt zu dominieren. Das Paradigma kam in der 19. Websearch-Engine zum Einsatz und etablierte sich innerhalb weniger Jahre und ist bis heute noch relevant. Google verwendete MapReduce auf verschiedene Weise, um die Websuche zu verbessern. Es wurde verwendet, um den Seiteninhalt zu indexieren und ein Ranking über die Relevant einer Webseite zu berechnen.

Dieses  Beispiel  zeigt  uns  den MapReduce-Algorithmus, mit dem Google Wordcount auf Webseiten ausführte. Die Map-Methode verwendet als Eingabe einen Schlüssel (key) und einen Wert, wobei der Schlüssel den Namen des Dokuments darstellt  und  der  Wert  der  Kontext  dieses Dokuments ist. Die Map-Methode durchläuft jedes Wort im Dokument und gibt es als Tuple zurück, die aus dem Wort und dem Zähler 1 besteht.

Die   Reduce-Methode   nimmt   als   Eingabe auch  einen  Schlüssel  und  eine  Liste  von  Werten an, in der der Schlüssel ein Wort darstellt. Die  Liste  von  Werten  ist  die  Liste  von  Zählungen dieses Worts. In diesem Beispiel ist der Wert 1. Die Methode “Reduce” durchläuft alle Zählungen. Wenn die Schleife beendet ist, um die Methode zu reduzieren, wird sie als Tuple zurückgegeben, die aus dem Wort und seiner Gesamtanzahl besteht.

 

Der Blick für das Wesentliche: Die Merkmalsselektion

In vielen Wissensbasen werden Datensätze durch sehr große Merkmalsräume beschrieben. Während der Generierung einer Wissensbasis wird versucht jedes mögliche Merkmal zu erfassen, um einen Datensatz möglichst genau zu beschreiben. Dabei muss aber nicht jedes Merkmal einen nachhaltigen Wert für das Predictive Modelling darstellen. Ein Klassifikator arbeitet mit reduziertem Merkmalsraum nicht nur schneller, sondern in der Regel auch weitaus effizienter. Oftmals erweist sich ein automatischer Ansatz der Merkmalsselektion besser, als ein manueller, da durchaus Zusammenhänge existieren können, die wir selbst so nicht identifizieren können.

Die Theorie: Merkmalsselektion

Automatische Merkmalsselektionsverfahren unterscheiden 3 verschiedene Arten: Filter, Wrapper und Embedded Methods. Einen guten Überblick über Filter- und Wrapper-Verfahren bieten Kumari et al. in ihrer Arbeit “Filter versus wrapper feature subset selection in large dimensionality micro array: A review” (Download als PDF).

Der Filter-Ansatz bewertet die Merkmale unabhängig des Klassifikators. Dabei werden univariate und multivariate Methoden unterschieden. Univariate Methoden bewerten die Merkmale separat, während der multivariate Ansatz mehrere Merkmale kombiniert. Für jedes Merkmal bzw. jedes Merkmalspaar wird ein statistischer Wert berechnet, der die Eignung der Merkmale für die Klassifikation angibt. Mithilfe eines Schwellwertes werden dann geeignete Merkmale herausgefiltert. Der Filter-Ansatz bietet eine schnelle und, aufgrund der geringen Komplexität, leicht skalierbare Lösung für die Merkmalsselektion. Der Nachteil von Filter-Selektoren besteht in der Missachtung der Abhängigkeiten zwischen den Merkmalen. So werden redundante Merkmale ähnlich bewertet und verzerren später die Erfolgsrate des Klassifikators. Bekannte Beispiele für Filter-Selektoren sind unter anderem die Euklidische Distanz und der Chi-2-Test.

Der Wrapper-Ansatz verbindet die Merkmalsbewertung mit einem Klassifikator. Innerhalb des Merkmalsraumes werden verschiedene Teilmengen von Merkmalen generiert und mithilfe eines trainierten Klassifikators getestet. Um alle möglichen Teilmengen des Merkmalsraumes zu identifizieren, wird der Klassifikator mit einem Suchalgorithmus kombiniert. Da der Merkmalsraum mit Zunahme der Anzahl der Merkmale exponentiell steigt, werden heuristische Suchmethoden für die Suche nach optimalen Teilmengen genutzt. Im Gegensatz zu den Filtern können hier redundante Merkmale abgefangen werden. Die Nutzung eines Klassifikators zur Bewertung der Teilmengen ist zugleich Vor- und Nachteil. Da die generierte Teilmenge auf einen speziellen Klassifikator zugeschnitten wird, ist nicht gewährleistet, dass die Menge auch für andere Klassifikatoren optimal ist. Somit ist dieser Ansatz zumeist abhängig vom gewählten Klassifikator. Zudem benötigt der Wrapper-Ansatz eine viel höhere Rechenzeit. Wrapper-Selektoren werden beispielsweise durch Genetische Algorithmen und Sequentielle Forward/Backward-Selektoren vertreten.

Embedded-Ansätze stellen eine Sonderform der Wrapper-Methode da. Allerdings werden Merkmalssuche und Klassifikatoren-Training nicht getrennt. Die Suche der optimalen Teilmenge ist hier im Modelltraining eingebettet. Dadurch liefern Embedded-Ansätze die gleichen Vorteile wie die Wrapper-Methoden, während die Rechenzeit dabei erheblich gesenkt werden kann. Der reduzierte Merkmalsraum ist aber auch hier vom jeweiligen Klassifikator abhängig. Klassifikatoren, die den Embedded-Ansatz ermöglichen sind beispielsweise der Random-Forest oder die Support-Vector-Maschine.

Entwicklungsgrundlage

Analog zum letzten Tutorial wird hier Python(x,y) und die Datenbasis „Human Activity Recognition Using Smartphones“ genutzt. Die Datenbasis beruht auf erfassten Sensordaten eines Smartphones während speziellen menschlichen Aktivitäten: Laufen, Treppen hinaufsteigen, Treppen herabsteigen, Sitzen, Stehen und Liegen. Auf den Aufzeichnungen von Gyroskop und Accelerometer wurden mehrere Merkmale erhoben. Die Datenmenge, alle zugehörigen Daten und die Beschreibung der Daten sind frei verfügbar.

(https://archive.ics.uci.edu/ml/datasets/Human+Activity+Recognition+Using+Smartphones)

Alle Daten liegen im Textformat vor. Für ein effizienteres Arbeiten mit der Datenbasis wurden diese im Vorfeld in das csv-Dateiformat überführt.

Python-Bibliotheken

Alle für das Data Mining relevanten Bibliotheken sind in Python(x,y) bereits enthalten. Für die Umsetzung werden folgende Bibliotheken genutzt:

Die Bibliotheken NumPy und Pandas unterstützen die Arbeit mit verschiedenen Datenstrukturen und scikit-learn umfasst alle Funktionen des maschinellen Lernens.

Daten vorbereiten

Vor der Anwendung der einzelnen Verfahren werden die Daten vorbereitet. Das Data Frame wird eingelesen, die Klassen in numerische Labels überführt und das Datenfeld in Merkmale (X) und Klassenspalte (y) separiert. Weiterhin wird die informationslose Spalte subject entfernt.

1. Verfahren: RFECV

Der RFECV (Recursive Feature Elimination with Cross Validation) ist ein Vertreter des Wrapper-Ansatzes. In diesem Beispiel wird die Merkmalsselektion mit einem Support Vector Klassifikator kombiniert. Der RFECV berechnet ein Ranking über die einzelnen Merkmale. Dabei bestimmt der Selektor selbst die optimale Menge der Merkmale. Alle Merkmale mit Platz 1 im Ranking bilden den optimalen Merkmalsraum.

2. Verfahren: Random Forest-Klassifikator

Der Random-Forest-Klassifikator gehört zu den Modellen, die einen Embedded-Ansatz ermöglichen. Während des Klassifikatoren-Trainings wird jedem Merkmal ein Wert zugeordnet. Je höher der Wert, desto bedeutsamer das Merkmal. Allerdings ist hier eine manuelle Filterung notwendig, da anders als beim RFECV kein internes Optimum ermittelt wird. Mithilfe eines geeigneten Schwellwertes können die zu wählenden Merkmale bestimmt werden. In diesem Beispiel werden alle Merkmale selektiert, die eine Wichtung größer dem Mittelwert erhalten.

3. Verfahren: Select K Best

Das Select K Best-Verfahren gehört den Filter-Ansätzen an. Daher kommt hier anders als bei den anderen beiden Verfahren kein Klassifikator zum Einsatz. Auch in diesem Verfahren wird für jedes Merkmal ein Wert berechnet, der die Wichtigkeit des Merkmals beziffert. Für die Berechnung der Werte können verschiedene Methoden verwendet werden. In diesem Beispiel wird eine Varianzanalyse genutzt (Parameter f_classif). Auch hier wird mithilfe eines manuellen Schwellwertes der reduzierte Merkmalsraum bestimmt.

Ergebnisse

Für die Bewertung der einzelnen Selektionsverfahren werden die einzelnen Verfahren in den Data-Mining-Prozess (siehe vorheriges Tutorial: Einstieg in das maschinelle Lernen mit Python(x,y)) integriert. Die nachfolgende Tabelle veranschaulicht die Ergebnisse der Klassifikation der einzelnen Verfahren.

 

Selektionsverfahren

Anzahl der Merkmale

Erfolgsrate Klassifikation

Ohne

561

93,96%

RFECV

314

94,03%

Random Forest

118

90,43%

Select K Best

186

92,30%

 

Durch den RFECV konnte das Ergebnis der Klassifikation leicht verbessert werden. Die anderen Selektionsverfahren, die auch deutlich weniger Merkmale nutzen, verschlechtern das Ergebnis sogar. Dies liegt vor allem an der manuellen Regulierung des Schwellwertes.

R Data Frames meistern mit dplyr – Teil 2

Dieser Artikel ist Teil 2 von 2 aus der Artikelserie R Data Frames meistern mit dplyr.

Noch mehr Datenbank-Features

Im ersten Teil dieser Artikel-Serie habe ich die Parallelen zwischen Data Frames in R und Relationen in SQL herausgearbeitet und gezeigt, wie das Paket dplyr eine Reihe von SQL-analogen Operationen auf Data Frames standardisiert und optimiert. In diesem Teil möchte ich nun drei weitere Analogien aufzeigen. Es handelt sich um die

  • Window Functions in dplyr als Entsprechung zu analytischen Funktionen in SQL,
  • Joins zwischen Data Frames als Pendant zu Tabellen-Joins
  • Delegation von Data Frame-Operationen zu einer bestehenden SQL-Datenbank

Window Functions

Im letzten Teil habe ich gezeigt, wie durch die Kombination von group_by() und summarise() im Handumdrehen Aggregate entstehen. Das Verb group_by() schafft dabei, wie der Name schon sagt, eine Gruppierung der Zeilen des Data Frame anhand benannter Schlüssel, die oft ordinaler oder kategorialer Natur sind (z.B. Datum, Produkt oder Mitarbeiter).

Ersetzt man die Aggregation mit summarise() durch die Funktion mutate(), um neue Spalten zu bilden, so ist der Effekt des group_by() weiterhin nutzbar, erzeugt aber „Windows“, also Gruppen von Datensätzen des Data Frames mit gleichen Werten der Gruppierungskriterien. Auf diesen Gruppen können nun mittels mutate() beliebige R-Funktionen angewendet werden. Das Ergebnis ist im Gegensatz zu summarise() keine Verdichtung auf einen Datensatz pro Gruppe, sondern eine Erweiterung jeder einzelnen Zeile um neue Werte. Das soll folgendes Beispiel verdeutlichen:

Das group_by() unterteilt den Data Frame nach den 4 gleichen Werten von a. Innerhalb dieser Gruppen berechnen die beispielsweise eingesetzten Funktionen

  • row_number(): Die laufende Nummer in dieser Gruppe
  • n(): Die Gesamtgröße dieser Gruppe
  • n_distinct(b): Die Anzahl verschiedener Werte von b innerhalb der Gruppe
  • rank(desc(b)): Den Rang innerhalb der selben Gruppe, absteigend nach b geordnet
  • lag(b): Den Wert von b der vorherigen Zeile innerhalb derselben Gruppe
  • lead(b): Analog den Wert von b der folgenden Zeile innerhalb derselben Gruppe
  • mean(b): Den Mittelwert von b innerhalb der Gruppe
  • cumsum(b): Die kumulierte Summe der b-Werte innerhalb der Gruppe.

Wichtig ist hierbei, dass die Anwendung dieser Funktionen nicht dazu führt, dass die ursprüngliche Reihenfolge der Datensätze im Data Frame geändert wird. Hier erweist sich ein wesentlicher Unterschied zwischen Data Frames und Datenbank-Relationen von Vorteil: Die Reihenfolge von Datensätzen in Data Frames ist stabil und definiert. Sie resultiert aus der Abfolge der Elemente auf den Vektoren, die die Data Frames bilden. Im Gegensatz dazu haben Tabellen und Views keine Reihenfolge, auf die man sich beim SELECT verlassen kann. Nur mit der ORDER BY-Klausel über eindeutige Schlüsselwerte erreicht man eine definierte, stabile Reihenfolge der resultierenden Datensätze.

Die Wirkungsweise von Window Functions wird noch besser verständlich, wenn in obiger Abfrage das group_by(a) entfernt wird. Dann wirken alle genannten Funktionen auf der einzigen Gruppe, die existiert, nämlich dem gesamten Data Frame:

Anwendbar sind hierbei sämtliche Funktionen, die auf Vektoren wirken. Diese müssen also wie in unserem Beispiel nicht unbedingt aus dplyr stammen. Allerdings komplettiert das Package die Menge der sinnvoll anwendbaren Funktionen um einige wichtige Elemente wie cumany() oder n_distinct().

Data Frames Hand in Hand…

In relationalen Datenbanken wird häufig angestrebt, das Datenmodell zu normalisieren. Dadurch bekommt man die negativen Folgen von Datenredundanz, wie Inkonsistenzen bei Datenmanipulationen und unnötig große Datenvolumina, in den Griff. Dies geschieht unter anderem dadurch, dass tabellarische Datenbestände aufgetrennt werden Stammdaten- und Faktentabellen. Letztere beziehen sich über Fremdschlüsselspalten auf die Primärschlüssel der Stammdatentabellen. Durch Joins, also Abfragen über mehrere Tabellen und Ausnutzen der Fremdschlüsselbeziehungen, werden die normalisierten Tabellen wieder zu einem fachlich kompletten Resultat denormalisiert.

In den Data Frames von R trifft man dieses Modellierungsmuster aus verschiedenen Gründen weit seltener an als in RDBMS. Dennoch gibt es neben der Normalisierung/Denormalisierung andere Fragestellungen, die sich gut durch Joins beantworten lassen. Neben der Zusammenführung von Beobachtungen unterschiedlicher Quellen anhand charakteristischer Schlüssel sind dies bestimmte Mengenoperationen wie Schnitt- und Differenzmengenbildung.

Die traditionelle R-Funktion für den Join zweier Data Frames lautet merge(). dplyr erweitert den Funktionsumfang dieser Funktion und sorgt für sprechendere Funktionsnamen und Konsistenz mit den anderen Operationen.

Hier ein synthetisches Beispiel:

Nun gilt es, die Verkäufe aus dem Data Frame sales mit den Produkten in products zusammenzuführen und auf Basis von Produkten Bilanzen zu erstellen. Diese Denormalisierung geschieht durch das Verb inner_join() auf zweierlei Art und Weise:

Die Ergebnisse sind bis auf die Reihenfolge der Spalten und der Zeilen identisch. Außerdem ist im einen Fall der gemeinsame Schlüssel der Produkt-Id als prod_id, im anderen Fall als id enthalten. dplyr entfernt also die Spalten-Duplikate der Join-Bedingungen. Letzere wird bei Bedarf im by-Argument der Join-Funktion angegeben. R-Experten erkennen hier einen „Named Vector“, also einen Vektor, bei dem jedes Element einen Namen hat. Diese Syntax verwendet dplyr, um elegant die äquivalenten Spalten zu kennzeichnen. Wird das Argument by weggelassen, so verwendet dplyr im Sinne eines „Natural Join“ automatisch alle Spalten, deren Namen in beiden Data Frames vorkommen.

Natürlich können wir dieses Beispiel mit den anderen Verben erweitern, um z.B. eine Umsatzbilanz pro Produkt zu erreichen:

dplyr bringt insgesamt 6 verschiedene Join-Funktionen mit: Neben dem bereits verwendeten Inner Join gibt es die linksseitigen und rechtsseitigen Outer Joins und den Full Join. Diese entsprechen genau der Funktionalität von SQL-Datenbanken. Daneben gibt es die Funktion semi_join(), die in SQL etwa folgendermaßen ausgedrückt würde:

Das Gegenteil, also ein NOT EXISTS, realisiert die sechste Join-Funktion: anti_join(). Im folgenden Beispiel sollen alle Produkte ausgegeben werden, die noch nie verkauft wurden:

… und in der Datenbank

Wir schon mehrfach betont, hat dplyr eine Reihe von Analogien zu SQL-Operationen auf relationalen Datenbanken. R Data Frames entsprechen Tabellen und Views und die dplyr-Operationen den Bausteinen von SELECT-Statements. Daraus ergibt sich die Möglichkeit, dplyr-Funktionen ohne viel Zutun auf eine bestehende Datenbank und deren Relationen zu deligieren.

Mir fallen folgende Szenarien ein, wo dies sinnvoll erscheint:

  • Die zu verarbeitende Datenmenge ist zu groß für das Memory des Rechners, auf dem R läuft.
  • Die interessierenden Daten liegen bereits als Tabellen und Views auf einer Datenbank vor.
  • Die Datenbank hat Features, wie z.B. Parallelverarbeitung oder Bitmap Indexe, die R nicht hat.

In der aktuellen Version 0.5.0 kann dplyr nativ vier Datenbank-Backends ansprechen: SQLite, MySQL, PostgreSQL und Google BigQuery. Ich vermute, unter der Leserschaft des Data Science Blogs dürfte MySQL (oder der Fork MariaDB) die weiteste Verbreitung haben, weshalb ich die folgenden Beispiele darauf zeige. Allerdings muss man beachten, dass MySQL keine Window Funktionen kennt, was sich 1:1 auf die Funktionalität von dplyr auswirkt.

Im folgenden möchte ich zeigen, wie dplyr sich gegen eine bestehende MySQL-Datenbank verbindet und danach einen bestehenden R Data Frame in eine neue Datenbanktabelle wegspeichert:

Die erste Anweisung verbindet R mit einer bestehenden MySQL-Datenbank. Danach lade ich den Data Frame diamonds aus dem Paket ggplot2. Mit str() wird deutlich, dass drei darin enthaltene Variablen vom Typ Factor sind. Damit dplyr damit arbeiten kann, werden sie mit mutate() in Character-Vektoren gewandelt. Dann erzeugt die Funktion copy_to() auf der MySQL-Datenbank eine leere Tabelle namens diamonds, in die die Datensätze kopiert werden. Danach erhält die Tabelle noch drei Indexe (von dem der erste aus drei Segmenten besteht), und zum Schluß führt dplyr noch ein ANALYSE der Tabelle durch, um die Werteverteilungen auf den Spalten für kostenbasierte Optimierung zu bestimmen.

Meistens aber wird bereits eine bestehende Datenbanktabelle die interessierenden Daten enthalten. In diesem Fall lautet die Funktion zum Erstellen des Delegats tbl():

Die Rückgabewerte von copy_to() und von tbl() sind natürlich keine reinrassigen Data Frames, sondern Objekte, auf die die Operationen von dplyr wirken können, indem sie auf die Datenbank deligiert werden. Im folgenden Beispiel sollen alle Diamanten, die ein Gewicht von mindestens 1 Karat haben, pro Cut, Color und Clarity nach Anzahl und mittlerem Preis bilanziert werden:

Die Definition der Variablen bilanz geschieht dabei komplett ohne Interaktion mit der Datenbank. Erst beim Anzeigen von Daten wird das notwendige SQL ermittelt und auf der DB ausgeführt. Die ersten 10 resultierenden Datensätze werden angezeigt. Mittels der mächtigen Funktion explain() erhalten wir das erzeugte SQL-Kommando und sogar den Ausführungsplan auf der Datenbank. SQL-Kundige werden erkennen, dass die verketteten dplyr-Operationen in verschachtelte SELECT-Statements umgesetzt werden.

Zu guter Letzt sollen aber meistens die Ergebnisse der dplyr-Operationen irgendwie gesichert werden. Hier hat der Benutzer die Wahl, ob die Daten auf der Datenbank in einer neuen Tabelle gespeichert werden sollen oder ob sie komplett nach R transferiert werden sollen. Dies erfolgt mit den Funktionen compute() bzw. collect():

Durch diese beiden Operationen wurde eine neue Datenbanktabelle „t_bilanz“ erzeugt und danach der Inhalt der Bilanz als Data Frame zurück in den R-Interpreter geholt. Damit schließt sich der Kreis.

Fazit

Mit dem Paket dplyr von Hadley Wickham wird die Arbeit mit R Data Frames auf eine neue Ebene gehoben. Die Operationen sind konsistent, vollständig und performant. Durch den Verkettungs-Operator %>% erhalten sie auch bei hoher Komplexität eine intuitive Syntax. Viele Aspekte der Funktionalität lehnen sich an Relationale Datenbanken an, sodass Analysten mit SQL-Kenntnissen rasch viele Operationen auf R Data Frames übertragen können.

Zurück zu R Data Frames meistern mit dplyr – Teil 1.