<jupyter_start><jupyter_text>Table of Contents0.0.1&nbsp;&nbsp;Installing Required Libraries0.0.1.1&nbsp;&nbsp;Getting Familiar With Jupyter Notebooks1&nbsp;&nbsp;Week 1: Basic Python Operations for Working with Text2&nbsp;&nbsp;The Scale of Data in the 21st Century2.1&nbsp;&nbsp;Overview2.1.0.1&nbsp;&nbsp;Text Analytics2.1.0.2&nbsp;&nbsp;Data Engineering2.1.0.3&nbsp;&nbsp;Statistics / Machine Learning2.2&nbsp;&nbsp;Loading Text into Memory2.2.0.1&nbsp;&nbsp;Opening Files2.2.1&nbsp;&nbsp;An Aside: List Comprehension2.2.2&nbsp;&nbsp;Visualizing Summary Metrics Using Matplotlib2.2.3&nbsp;&nbsp;First Method: Create a Dictionary to Store Word Count2.2.4&nbsp;&nbsp;Using Python's Built-In Counter2.2.5&nbsp;&nbsp;In-Class Question2.3&nbsp;&nbsp;Zipf's Law2.3.1&nbsp;&nbsp;General Definition2.3.2&nbsp;&nbsp;Approximation in NLP3&nbsp;&nbsp;Regular Expressions3.0.1&nbsp;&nbsp;Match the first time a capital letter appears in the tweet3.0.2&nbsp;&nbsp;Match all capital letters that appears in the tweet3.0.3&nbsp;&nbsp;Match all words that are at least 3 characters long3.0.4&nbsp;&nbsp;Word Boundaries3.0.5&nbsp;&nbsp;Removing Stopwords Using Regex3.0.5.1&nbsp;&nbsp;Exercises4&nbsp;&nbsp;Encoding Schemes4.0.1&nbsp;&nbsp;Implications for Data Science4.1&nbsp;&nbsp;World Languages, in Context4.2&nbsp;&nbsp;Bits and Bytes4.2.1&nbsp;&nbsp;Exercises4.3&nbsp;&nbsp;ASCII4.3.1&nbsp;&nbsp;Encoding/Decoding Words4.3.1.1&nbsp;&nbsp;Steps:4.3.2&nbsp;&nbsp;Python Code4.3.3&nbsp;&nbsp;Extended ASCII4.3.3.1&nbsp;&nbsp;Latin-14.3.3.2&nbsp;&nbsp;Excel on Macs4.3.4&nbsp;&nbsp;Turning Strings into Bytes4.3.5&nbsp;&nbsp;Turning Bytes into Strings4.3.5.1&nbsp;&nbsp;Exercises4.4&nbsp;&nbsp;Unicode4.4.1&nbsp;&nbsp;UTF-84.4.1.1&nbsp;&nbsp;Getting the Codepoint of a Character4.4.1.2&nbsp;&nbsp;Getting the Character from a Codepoint4.4.2&nbsp;&nbsp;Variable Length Encoding /Digitalization and Internationalization4.4.2.1&nbsp;&nbsp;Data Science Implications5&nbsp;&nbsp;Homework 1 (Due Monday March 23rd, 2020 at 11:59pm PST)5.1&nbsp;&nbsp;Next Week (March 24th)5.1.1&nbsp;&nbsp;Check for Understanding  Installing Required Libraries<jupyter_code>!pip3 install matplotlib
!pip3 install pandas<jupyter_output><empty_output><jupyter_text>Getting Familiar With Jupyter Notebooks Jupyter keyboard shortcuts:- Press `Esc` to go into **Command Mode**. Your cell should turn from green highlights to blue highlights.- In **Command Mode**, press `M` to go into `Markdown` mode. This turns your cell into Markdown text so you can type text.- Press `Y` to go into `Code` mode. This then allows you to begin typing Python code.- Press `A` to insert a cell above your current cell.- Press `B` to insert a cell below your current cell.- Press `D` twice to delete your current cell.- Press `Shift` + `Enter` to save your cell.  Week 1: Basic Python Operations for Working with Text  The Scale of Data in the 21st Century      ASCII table converting numbers to characters.(Wikipedia)  OverviewBy the end of this week, you should be able to perform the following operations: Text Analytics- **load a text file into memory** using Python's built-in streaming libraries- **visualize word count and line length distributions** as histograms using Matplotlib Data Engineering- **read strings from a text input/output stream** using `readline()` and `readlines()`- **use both native Python dictionaries and `collections.Counter` objects** to produce word counts for a text corpus- perform basic search/replace operations using **regular expressions**- encode/decode text from bytes to support internationalization and digital-native characters (such as **emojis**). Statistics / Machine Learning- **create a word transition matrix using Numpy arrays**, which can be used for probabilistic inference and text generation (we will cover Week 2) Loading Text into MemoryThere are a variety of ways to hold data within memory. For text analytics and natural language processing purposes, we'll be most concerned with the following:- **list**- **set**- **dictionary**- **tuple**- **Numpy array**Imagine that we would like to find the most commonly used words in ***A Tale of Two Cities***, by the famed English novelist <NAME>, stored in a text file called **`tale-of-two-cities.txt`**, in the same directory as this Jupyter notebook. Later on, we'll use 3rd-party libraries to automate much of the processing, but for now, we'll explore Python's built-in functions for text processing. Opening Files The **`open()`** function takes *two* parameters; **filename**, and **mode**. In our case, `mode` is set to `r` for **read**, since we plan to read the file's contents, as opposed to `w` (write), or `a` (append).<jupyter_code># Open Tale of Two Cities
text_file = open("tale-of-two-cities.txt", "r")
print(text_file)<jupyter_output><_io.TextIOWrapper name='tale-of-two-cities.txt' mode='r' encoding='UTF-8'><jupyter_text>Typically, a text character is **1 byte** in size. One byte is equal to **8 bits**. This means conceptually, the size of a string should be $N$ bytes, where $N$ is the number of characters. However, you'll see that in Python, the size of a string is larger:<jupyter_code>import sys
EMPTY_STRING = ""
ONE_CHAR_STRING = "a"
TWO_CHAR_STRING = "ab"
print(f"The size of EMPTY_STRING is {sys.getsizeof(EMPTY_STRING)} bytes.")
print(f"The size of ONE_CHAR_STRING is {sys.getsizeof(ONE_CHAR_STRING)} bytes.")
print(f"The size of TWO_CHAR_STRING is {sys.getsizeof(TWO_CHAR_STRING)} bytes.")<jupyter_output>The size of EMPTY_STRING is 49 bytes.
The size of ONE_CHAR_STRING is 50 bytes.
The size of TWO_CHAR_STRING is 51 bytes.<jupyter_text>The **`open()`** function returns a **`TextIOWrapper`** object from Python's `io` module, which handles common input/output streaming operations. A **stream** is a potentially infinite sequence of elements (in our case, characters) arriving over time. You'll use streams to model data that is **unbounded** (it's undetermined the volume, the length, and frequency of the data). A stream has a pointer to its current position within the sequence. This object has an extremely helpful **`readline()`** method that reads from a text file until encountering an **`EOF`** marker or a new line symbol.<jupyter_code>text_file.readline()<jupyter_output><empty_output><jupyter_text>You can pass in a parameter to **`readline()`** to control how many bytes of input stream data you'll receive. For instance, **`readline(2)`** returns at most 2 bytes of text input data. You might use this, for instance, if your Python application is reading not from a flat text file, but from a socket, which supplies a continuous stream of data with fixed length (ie., the messages all have the same number of characters).**In-Class Question**: *Assume you just opened the text file with **`open()`**. What output is returned when **`text_file.readline(5)`** is called the **second** time?*- **A)** The entire first line of the novel- **B)** The first 5 characters of the second line- **C)** The entire second line of the novel- **D)** The first 5 characters of the first line- **E)** The 6th-10th characters of the first line<jupyter_code>text_file.seek(0) #reset the stream position to the start of the text file
for i in range(2): # repeat the below line twice
    print(f"Iteration {i + 1}: {text_file.readline(5)}")<jupyter_output>Iteration 1:   IT 
Iteration 2: WAS t<jupyter_text>Each time that you call **`readline()`**, a position marker within **`TextIOWrapper`** is moved forward:We typically will use **`readlines()`** instead to read text files line by line. This returns a Python **list**:<jupyter_code>text_file.seek(0) # reset the stream position to the start of the file
lines = text_file.readlines() # read all the lines and return a list of strings<jupyter_output><empty_output><jupyter_text>We see that there are **12870** lines of text in the novel.<jupyter_code>print(f"There are {len(lines)} lines in the novel.")
total_num_chars = 0
for line in lines: # iterate through each line
    total_num_chars += len(line) # add the number of characters in a line to the total count of characters

avg_chars = round(total_num_chars / len(lines),1) # divide total character count by number of lines to get average
print(f"On average, each line has {avg_chars} characters.")
import matplotlib.pyplot as plt # we are importing the pyplot module from matplotlib, and naming it as plt<jupyter_output><empty_output><jupyter_text>An Aside: List ComprehensionSometimes, we need to iterate through a list and perform some sort of operation (sum all the elements, or remove a certain character). The traditional way to do this is using a for loop:```Pythonlengths = []  declare an empty listfor line in lines:  iterate through each line    lengths.append(len(line))  add the length of each line to the list```A slightly less verbose way, called **list comprehension**, to write this is```Pythonlengths = [len(line) for line in lines]```List comprehension is **typically slightly faster**, since it avoids the additional `append()` call for each iteration of the for loop. See this example from StackOverflow:```Pythondef slower():  using traditional iteration    result = []    for elem in some_iterable:        result.append(elem)    return result``````Pythondef faster():  using list comprehension    return [elem for elem in some_iterable]```Within the Python REPL **(read-eval-print-loop)**:```Python>>> some_iterable = range(1000)>>> import timeit>>> timeit.timeit('f()', 'from __main__ import slower as f', number=10000)1.4456570148468018>>> timeit.timeit('f()', 'from __main__ import faster as f', number=10000)0.49323201179504395```  Visualizing Summary Metrics Using Matplotlib<jupyter_code>NUM_BINS = 30 # increase this number to make the visualization more granular
plt.rcParams["figure.figsize"] = (15,6)
plt.hist([len(line) for line in lines], bins=NUM_BINS)
plt.title("Distribution of Line Lengths in Tale of Two Cities") # give the plot a title
plt.xlabel("Number of Characters in Line") # label the X axis
plt.ylabel("Count of Lines") # label the Y axis
plt.show()<jupyter_output><empty_output><jupyter_text>What if now we want to visualize how many times each word appears in the entire novel (for now, we won't worry about **stemming / lemmatization** and other preprocessing steps)? First Method: Create a Dictionary to Store Word CountDictionaries in Python have **keys** and **values**. The keys must be unique (no duplicate keys). They can be accessed via the **`keys()`** and **`values()`** methods of a dictionary object.<jupyter_code>words = [] # create a list of all words
word_count = {} # create a dictionary to store word counts

for line in lines: # for each line in the novel
    for word in line.split(" "): # for each word in the line
        words.append(word) # add the word to the list of words
        
        if word not in word_count.keys(): # if the word has not been seen before, add it to the dictionary with initial count of 1
            word_count[word] = 1
        else:
            word_count[word] += 1 # if the word has been seen before, increment its count by 1
        
print(f"There's an estimated {len(words)} words in the novel.")
print(f"There's {len(word_count.keys())} unique words in the novel.")<jupyter_output><empty_output><jupyter_text>Let's use Python **`sets`** to check that our dictionary's keys are unique. Remember that a set is a collection of **unique elements**, so calling **`set(words)`** will return only the unique words in our text file.<jupyter_code>assert len(word_count.keys()) == len(set(words)), "This error message will be printed if the assertion to the left is not true."<jupyter_output><empty_output><jupyter_text>Using Python's Built-In CounterSince the task of building a count using a dictionary is a common operation, Python provides a built-in object called `Counter` that we can use:<jupyter_code>from collections import Counter

def count_words(lines, delimiter=" "):
    
    words = Counter() # instantiate a Counter object called words
    for line in lines:
        for word in line.split(delimiter):
            words[word] += 1 # increment count for word
    return words<jupyter_output><empty_output><jupyter_text>A core principle of software engineering and programming is **DRY**: Don't Repeat Yourself. Since we are likely going to be making many histograms throughout this course, it's best that we create a reusable function.<jupyter_code>def make_histogram(values, title=None,xlabel=None,ylabel=None, bins=30, x_size=15, y_size=6):
    plt.rcParams["figure.figsize"] = (x_size,y_size)
    plt.hist(values, bins=bins)
    
    if title:
        plt.title(title) # give the plot a title
        
    if xlabel:
        plt.xlabel(xlabel) # label the X axis
        
    if ylabel:
        plt.ylabel(ylabel) # label the Y axis
        
    plt.show() 
make_histogram(word_count.values(), 
               title="Distribution of Word Count",
               xlabel="Number of Times Word Appears",
               ylabel="Number of Unique Words")<jupyter_output><empty_output><jupyter_text>In-Class Question- Why does this distribution look the way it does? - What additional steps could be taken to make the results more meaningful?<jupyter_code>import pandas as pd # output the results to a dataframe
word_count_df = pd.DataFrame(columns=["word", "frequency"]) # create a dataframe with two columns, word and frequency

word_count_df["word"] = list(word_count.keys())
word_count_df["frequency"] = list(word_count.values())
word_count_df.to_csv("dickens_word_count.csv") # saves to an outputs folder - if you don't have one, Python will throw an error<jupyter_output><empty_output><jupyter_text>Zipf's Law General DefinitionZipf's Law states that for `N` words, the `k`th most frequent word will appear with a normalized frequency equal to  The parameter $s$ is an exponent that defines the behavior of the distribution. Traditionally, in natural language, $s = 1$.    <NAME>, http://zipfr.r-forge.r-project.org/materials/LREC2018/tutorial_lrec2018.handout.pdf Approximation in NLPIf $t_1$ is the most common word in a collection of text, and $t_2$ is the next most common word, then the frequency of the $i$th most common word is proportional to $\frac{1}{i}$. The approximation we'll use specifically for natural languages is$$f(t_i) = \frac{0.1}{i^\alpha}$$$\alpha = 1$.To represent the frequency of a word in a body of text.In human language, there are **a few high-frequency words and many low-frequency words**. What does this mean in terms of machine learning / data modelling?* In many cases, the high frequency words do not carry much value in terms of predictive power or signal. These are frequently **stopwords** that must be removed / otherwise feature-engineered.  Regular Expressions<jupyter_code># get the top stopwords
word_count_df.sort_values(by=["frequency"], ascending=False).head(5)
import re

SAMPLE_TWEET = '''
#wolfram Alpha SUCKS! Even for researchers the information provided is less than you can get from 
#google or #wikipedia, totally useless! Avoid Wolfram at all costs, #ScrewWolframProducts"
'''

# create a dataframe version of Dickens' novel
dickens_text_df = pd.DataFrame( open("tale-of-two-cities.txt", "r"), columns=["line"])
dickens_text_df["line"] = dickens_text_df["line"].str.replace("\n", "")<jupyter_output><empty_output><jupyter_text>Match the first time a capital letter appears in the tweet<jupyter_code>match = re.search("[A-Z]", SAMPLE_TWEET)
match.group()<jupyter_output><empty_output><jupyter_text>Match all capital letters that appears in the tweet<jupyter_code># re
re.findall("[A-Z]", SAMPLE_TWEET)
# pandas
dickens_text_df["results"] = dickens_text_df["line"].str.extract(r'([A-Z])')<jupyter_output><empty_output><jupyter_text>Match all words that are at least 3 characters long<jupyter_code># re
re.findall("[a-zA-Z]{3,}", SAMPLE_TWEET)[:5] # show only the first 5
# pandas
dickens_text_df["results"] = dickens_text_df["line"].str.extract(r'([a-zA-Z]{3,})')
dickens_text_df["results"] =  dickens_text_df["line"].str.findall(r'([a-zA-Z]{3,})')
dickens_text_df.head(5)<jupyter_output><empty_output><jupyter_text>Word BoundariesConsider the sentence:*A thorough examination of the movie shows Thor was a thorn in the side of the villains, both then and now. thor.*What happens if you try to parse out all `Thor` references? What happens if you want to remove `A` or `a`, or `the` to clean up the text?<jupyter_code>text = "A thorough examination of the movie shows Thor was a thorn in the side of the villains, both then and now. thor."
# re
text = "A thorough examination of the movie shows Thor was a thorn in the side of the villains, both then and now. thor."
text = re.sub(r'(a|A)', '', text)
text
text = "A thorough examination of the movie shows Thor was a thorn in the side of the villains, both then and now. thor."
re.findall(r'\b(thor|Thor)\b', text) # notice the use of the r string prefix!
# pandas
dickens_text_df["results"] = dickens_text_df["line"].str.findall(r'\bthe\b', case=False)
dickens_text_df.head(5)<jupyter_output><empty_output><jupyter_text>Removing Stopwords Using Regex<jupyter_code># re
text = re.sub('(the|The)', '', text, flags=re.IGNORECASE)
text
# pandas

dickens_text_df["results"] = dickens_text_df["line"].str.replace(r'\bthe\b', '', case=False)
dickens_text_df.head()<jupyter_output><empty_output><jupyter_text>Exercises1. One of the main characters in A Tale of Two Cities is `Sydney Carton`. How many times is the word `Carton` used?2. How many times does the word `the` appear in the novel?3. How would you find replace the stopword `the` using regex from `Tale of Two Cities`3. What percentage of lines in Dickens' text contain adverbs? For now, you can classify an adverb as a word that ends in `ly`.4. One many times does <NAME> use the pattern `WORD, WORD, and WORD` in this novel (for example `red, bluff, and free`)?  Encoding SchemesThis is not a computer science class, but in your practical work, you will frequently have to deal with text that is encoded in a variety of styles. Understanding the difference between them is key. Implications for Data ScienceReasons I've encountered in my own work with NLP why it is beneficial to understand text encoding schemes:* If you are not using the right encoding, you cannot perform adequate feature engineering* Some data-scientists have simply "thrown away" samples of tweets, social media comments that seem "mal-formed" but are actually simply just using a different encoding scheme World Languages, in Context   Bits and Bytes- Computers, at its lowest level, store everything in the form of bits (either a 0 or a 1). The amount of information that can be represented in a computer is determined by the number of bits.  For instance, a using only 4 bits, you can store **$2^4$ = 16** different values.      How $101010$ is converted to decimal (human-readable numbers): each of the green numbers is summed up to equal 42.It is not physically efficient for a computer to try to read one bit at a time, so typically data is stored in **8-bit** groups called **bytes**. Exercises1. How many bits (0s and 1s) does it take to represent **4 different characters**?2. How many bits (0s and 1s does it take to represent **128 different characters**? ASCIIThe oldest, yet still relevant encoding style to be aware of is **ASCII**, where computers represent text (**every character on a keyboard**) initially as a number between 0 and 127 (question: how many bits does it take to do this?)      ASCII table converting numbers to characters.(Wikipedia)*If the smallest amount of data a computer can realistically read in is a byte (**8-bits**), why is ASCII only **7-bits**?* The last bit was a **parity bit** is used for **error checking** - to ensure that the data wasn't corrected or unintentionally altered. Encoding/Decoding WordsHow would you write the word `Data` using ASCII encoding? Steps:1. Look up the "codepoint" for the first character (`D`).When you look up the character map value for `D`, its corresponding codepoint is `68`. Note that this is a different codepoint than lowercase `d`.2. Write out that number in binary.    The ASCII binary encoding for `D` is `1000100`.3. Repeat for `a`, `t`, and `a`.Use [this website to check your answer](https://www.rapidtables.com/convert/number/ascii-to-binary.html).<jupyter_code># to find out your computer's default encoding system
sys.getdefaultencoding()<jupyter_output><empty_output><jupyter_text>Python CodeDo not worry about understanding what is happening inside the `get_binary_for_char` and `get_binary` functions. Just know that they take in a string and produce the 0s and 1s that the string is encoded in:<jupyter_code>import re
from typing import List

def get_binary_for_char(char: str, encoding="utf-8") -> str:
    """
    Encodes a character using the desired encoding into its corresponding hex, then converts the
    hex code into binary, formatted with tab spaces between byte marks.
    """

    hex_code = char.encode(encoding).hex()
    code_point = hex(ord(char))[2:].upper()

    binary: str = f"{int(hex_code, 16):08b}"

    byte_list: List[str] = re.findall('[01]{8}', binary)
    formatted_binary: str = "\t".join(byte_list)  # for variable length encoding, tab space between byte marks.
    print(f"{char} (U+{code_point.zfill(4)}, hex:{hex_code}) - {encoding}: {formatted_binary}")
    return formatted_binary


def get_binary(text: str, encoding="utf-8"):
    return " ".join([get_binary_for_char(char, encoding) for char in text])




# ASCII encoding of "Data"<jupyter_output><empty_output><jupyter_text>Extended ASCIIThe dominant language in earlier eras of computing was English. People began to realize that ASCII was relatively limited, and even other European languages could not be properly supported. At the same time, transmission technology evolved to a standard of reliability such that the parity bit used for checking for errors was no longer needed. As a result, people began using the last (eighth) bit to extend the number of characters represented by ASCII from 128 characters to 256 characters. Latin-1Character map [available here](https://www.htmlhelp.com/reference/charset/latin1.gif).Characters such as `Ç` (pronounced `ch` in Turkish, for instance), is represented by the number `199`. The Spanish word `año` (year) includes a character `ñ` that would be represented by the code point `241`. Excel on MacsMacs commonly use [Mac OS Roman encoding](https://en.wikipedia.org/wiki/Mac_OS_Roman).<jupyter_code>#latin1 encoding of "cat"


# encoding Cuántas<jupyter_output><empty_output><jupyter_text>In Python, to convert from bytes to strings, you use the `encode()` and `decode()` functions.  Turning Strings into Bytes<jupyter_code># Using Python's encode/decode:
print("Using latin1 encoding:", "más".encode(encoding="latin1"))
print("Using ASCII encoding:", "más".encode(encoding="ascii"))<jupyter_output>Using latin1 encoding: b'm\xe1s'<jupyter_text>Turning Bytes into Strings<jupyter_code>file = open("test.txt", mode="rb")
bytes_from_file = file.read()
print(type(bytes_from_file))
string_from_file = bytes_from_file.decode()
print(type(string_from_file))<jupyter_output><class 'bytes'>
<class 'str'><jupyter_text>Exercises1. Encode the word `más` in ASCII.2. Encode the word `más` in `latin1`.3. Decode the binary stream `01100011 01100001 01110100`. Assume that it is using `latin1` encoding.  Unicode128 characters is not enough to represent the characters in other Languages, like **Greek, Turkish, Cyrillic**, etc., or newer social media phenomenons like **emojis**. Unicode stores text as either 8, 16, or 32 bits (1, 2, or 4 bytes). This means there's significantly more characters that can be encoded (approximately 1 billion characters).As a point of reference, there's a total of **50,000** characters in the Chinese language (but only around **15-20,000** that are used commonly).If you don't specify the right encoding to read in text, you'll end up with something like this:      Malformed characters because of incorrect encoding.(Wikipedia) UTF-8The default encoding scheme of the internet today is `UTF-8`.      Share of web pages with different encodings.(Wikipedia)There is another encoding schema very similar to `UTF-8` called `UTF-16`. You'll typically find it being used on Windows systems and within Java applications.  Getting the Codepoint of a Character<jupyter_code>ord("a")<jupyter_output><empty_output><jupyter_text>Getting the Character from a Codepoint<jupyter_code>chr(97) + chr(109)<jupyter_output><empty_output><jupyter_text>Variable Length Encoding /Digitalization and InternationalizationUTF-8 is the default encoding schema of the internet. Whenever you save files to disk, or read files in, your first choice should be to try using UTF-8. UTF-8 is an example of **variable-length encoding**. This means sometimes a character will take 8 bits to encode (represent), sometimes 16 bits, sometimes 24 bits, and sometimes 32 bits.UTF-8 uses **continuation bytes** for any character it needs to represent beyond 1 byte. These bytes always start with `10`.      UTF-8 Continuation BytesOn the other hand, another encoding scheme is `UTF-32`- it always takes **32 bits**.  Data Science ImplicationsUTF-8 should be your default encoding of choice when working with Big Data. Because the  of bits it takes to encode a character changes, it can be more "storage-efficient" on disk, and more "memory-efficient" when representing this text in memory. Many machine-learning algorithms (like **batch and mini-batch gradient descent**) will perform updates using batches of samples  (if not the entire dataaset). If you choose the wrong encoding, you will not be able to fit as many samples into your batch for training as you'd like - this means your model may require significantly more training time and perform worse.<jupyter_code># get_binary("I 😍 DSO 599", encoding="ascii")
# get_binary("I 😍 DSO 599", encoding="latin1")
# get_binary("I 😍 DSO 599", encoding="utf8")<jupyter_output><empty_output>
StarcoderdataJupyterStructuredCleanDedup