<jupyter_start><jupyter_text>Numpy-practise

This notebook will contain some of the numpy exercises done in Lesson 02 of the course.<jupyter_code># Install dependencies here
#!pip install pandas
# Import dependencies

import unicodecsv
import pandas as pd
import numpy as np
daily_engagegment = pd.read_csv('../project-1/daily_engagement.csv')
len(daily_engagegment['acct'].unique())<jupyter_output><empty_output><jupyter_text>Numpy 1D Arrays<jupyter_code># Setting the data

# First 20 countries with employment data
countries = np.array([
    'Afghanistan', 'Albania', 'Algeria', 'Angola', 'Argentina',
    'Armenia', 'Australia', 'Austria', 'Azerbaijan', 'Bahamas',
    'Bahrain', 'Bangladesh', 'Barbados', 'Belarus', 'Belgium',
    'Belize', 'Benin', 'Bhutan', 'Bolivia',
    'Bosnia and Herzegovina'
])

# Employment data in 2007 for those 20 countries
employment = np.array([
    55.70000076,  51.40000153,  50.5       ,  75.69999695,
    58.40000153,  40.09999847,  61.5       ,  57.09999847,
    60.90000153,  66.59999847,  60.40000153,  68.09999847,
    66.90000153,  53.40000153,  48.59999847,  56.79999924,
    71.59999847,  58.40000153,  70.40000153,  41.20000076
])
# Accessing elements
print (countries[0])
print (countries[3])
# Slicing
print (countries[0:3])
print (countries[:3])
print (countries[17:])
print (countries[:])
# Element types
print (countries.dtype)  # U22, since it is a string array in Unicode, and length of the longest string is 22
print( employment.dtype)
print (np.array([0, 1, 2, 3]).dtype)
print (np.array([1.0, 1.5, 2.0, 2.5]).dtype)
print (np.array([True, False, True]).dtype)
print (np.array(['AL', 'AK', 'AZ', 'AR', 'CA']).dtype)  # U2, since it a string array with the length of the highest string being 2
# Looping
for country in countries:
    print (f'Examining country {country}')

for i in range(len(countries)):
    country = countries[i]
    country_employment = employment[i]
    print (f'Country {country} has employment {country_employment}')
# Numpy functions
print (employment.mean())
print (employment.std())
print (employment.max())
print (employment.sum())
    
print('length of countries:', len(countries))
print('length of employment:', len(employment))
def max_employment(countries, employment):
    '''
    Fill in this function to return the name of the country
    with the highest employment in the given employment
    data, and the employment in that country.
    '''
    
    max_country = None
    max_value = 0
    
    for country, employ in zip(countries, employment):
        if employ > max_value:
            max_value = employ
            max_country = country

    return (max_country, max_value)

print(max_employment(countries, employment))<jupyter_output>('Angola', 75.69999695)<jupyter_text>Arithmetic and Logical Operations in Numpy<jupyter_code># Arithmetic operations between 2 NumPy arrays
a = np.array([1, 2, 3, 4])
b = np.array([1, 2, 1, 2])

print (a + b)
print (a - b)
print (a * b)
print (a / b)
print (a ** b)
# Arithmetic operations between a NumPy array and a single number
a = np.array([1, 2, 3, 4])
b = 2

print (a + b)
print (a - b)
print (a * b)
print (a / b)
print (a ** b)<jupyter_output>[3 4 5 6]
[-1  0  1  2]
[2 4 6 8]
[0.5 1.  1.5 2. ]
[ 1  4  9 16]<jupyter_text>Note on the logical operations in the following cells

The operators used in the below cell for logical operations are `&`, `|` and `~`.
These qualify to be logical operators only because the arrays used are `bool` in datatype. For `bool` data in numpy, logical and bitwise operations are the same.

**For integer data, though, the above operators will not work as logical operators, and will instead perform bitwise operations.**

Therefore, if you want to perform logical operations on `int` arrays in numpy, used the following _methods_ instead:
- `np.logical_and(a,b)`
- `np.logical_or(a,b)`
- `np.logical_not(a)`<jupyter_code># Logical operations with NumPy arrays
a = np.array([True, True, False, False])
b = np.array([True, False, True, False])

print (a & b)
print (a | b)
print (~a)

print (a & True)
print (a & False)

print (a | True)
print (a | False)
# Comparison operations between 2 NumPy Arrays
a = np.array([1, 2, 3, 4, 5])
b = np.array([5, 4, 3, 2, 1])

print (a > b)
print (a >= b)
print (a < b)
print (a <= b)
print (a == b)
print (a != b)
# Comparison operations between a NumPy array and a single number
a = np.array([1, 2, 3, 4])
b = 2

print (a > b)
print (a >= b)
print (a < b)
print (a <= b)
print (a == b)
print (a != b)
# First 20 countries with school completion data
countries = np.array([
       'Algeria', 'Argentina', 'Armenia', 'Aruba', 'Austria','Azerbaijan',
       'Bahamas', 'Barbados', 'Belarus', 'Belgium', 'Belize', 'Bolivia',
       'Botswana', 'Brunei', 'Bulgaria', 'Burkina Faso', 'Burundi',
       'Cambodia', 'Cameroon', 'Cape Verde'
])

# Female school completion rate in 2007 for those 20 countries
female_completion = np.array([
    97.35583,  104.62379,  103.02998,   95.14321,  103.69019,
    98.49185,  100.88828,   95.43974,   92.11484,   91.54804,
    95.98029,   98.22902,   96.12179,  119.28105,   97.84627,
    29.07386,   38.41644,   90.70509,   51.7478 ,   95.45072
])

# Male school completion rate in 2007 for those 20 countries
male_completion = np.array([
     95.47622,  100.66476,   99.7926 ,   91.48936,  103.22096,
     97.80458,  103.81398,   88.11736,   93.55611,   87.76347,
    102.45714,   98.73953,   92.22388,  115.3892 ,   98.70502,
     37.00692,   45.39401,   91.22084,   62.42028,   90.66958
])

def overall_completion_rate(female_completion, male_completion):
    '''
    Fill in this function to return a NumPy array containing the overall
    school completion rate for each country. The arguments are NumPy
    arrays giving the female and male completion of each country in
    the same order.
    '''
    return (female_completion + male_completion) / 2.0

overall_completion_rate(female_completion, male_completion)<jupyter_output><empty_output><jupyter_text>Numpy index arrays

The index of an array can be another boolean array. The returned array will be a slice of the original array containing the elements whose indices corresponded to the `True` placed of the boolean array.<jupyter_code># Using index arrays
a = np.array([1, 2, 3, 4])
b = np.array([True, True, False, False])

print (a[b])
print (a[np.array([True, False, True, False])])
# Creating the index array using vectorized operations
a = np.array([1, 2, 3, 2, 1])
b = (a >= 2)

print (a[b])
print (a[a >= 2])
# Creating the index array using vectorized operations on another array
a = np.array([1, 2, 3, 4, 5])
b = np.array([1, 2, 3, 2, 1])

print (b == 2)
print (a[b == 2])
def mean_time_for_paid_students(time_spent, days_to_cancel):
    '''
    Fill in this function to calculate the mean time spent in the classroom
    for students who stayed enrolled at least (greater than or equal to) 7 days.
    Unlike in Lesson 1, you can assume that days_to_cancel will contain only
    integers (there are no students who have not canceled yet).
    
    The arguments are NumPy arrays. time_spent contains the amount of time spent
    in the classroom for each student, and days_to_cancel contains the number
    of days until each student cancel. The data is given in the same order
    in both arrays.
    '''
    return np.mean(time_spent[days_to_cancel >= 7])

# Time spent in the classroom in the first week for 20 students
time_spent = np.array([
       12.89697233,    0.        ,   64.55043217,    0.        ,
       24.2315615 ,   39.991625  ,    0.        ,    0.        ,
      147.20683783,    0.        ,    0.        ,    0.        ,
       45.18261617,  157.60454283,  133.2434615 ,   52.85000767,
        0.        ,   54.9204785 ,   26.78142417,    0.
])

# Days to cancel for 20 students
days_to_cancel = np.array([
      4,   5,  37,   3,  12,   4,  35,  38,   5,  37,   3,   3,  68,
     38,  98,   2, 249,   2, 127,  35
])

print(mean_time_for_paid_students(time_spent, days_to_cancel))<jupyter_output>41.05400348545454<jupyter_text>In-place vs Not in-place

In-place operations happen in the same variable. They update the values of the variables without creating a new variablle.

**Example:** The `+` operator creates a new array while adding two arrays, whereas the `+=` operator does the changes in-place.<jupyter_code># Testing +
a = np.array([1,2,3,4,5])
b = a
c = np.array([1,1,1,1,1])
print(f'a = {a} \nb = a = {b} \nc = {c}')
a = a + c
print(f'b (after a = a+c) = {b}')  # `b` will have no change, as `a+b` is a new array

print()

# Testing +=
a = np.array([1,2,3,4,5])
b = a
c = np.array([1,1,1,1,1])
print(f'a = {a} \nb = a = {b} \nc = {c}')
a += c
print(f'b (after a += c) = {b}')  # `b` will take the value of `a` as the operation is done in-place<jupyter_output>a = [1 2 3 4 5] 
b = a = [1 2 3 4 5] 
c = [1 1 1 1 1]
b (after a = a+c) = [1 2 3 4 5]

a = [1 2 3 4 5] 
b = a = [1 2 3 4 5] 
c = [1 1 1 1 1]
b (after a += c) = [2 3 4 5 6]<jupyter_text>**Another example.** Slicing is in-place in numpy arrays, but not in traditional Python lists.<jupyter_code># Slicing in Python lists
a = [1, 2, 3, 4, 5]
segment = a[:3]  # new array; not in place
segment[0] = 10
print(a)

a = np.array([1, 2, 3, 4, 5])
segment = a[:3]
segment[0] = 10  # change will happen in same array since in place
print(a)<jupyter_output>[1, 2, 3, 4, 5]
[10  2  3  4  5]
{'fraction_non_alphanumeric': 0.1292941911305434, 'fraction_numerical': 0.09108890276910264, 'mean_word_length': 3.132043010752688, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '280282', 'license': 'MIT', 'n_tokens_mistral': 4109, 'n_tokens_neox': 3489, 'n_words': 1178, 'path': 'project-2/numpy-practise.ipynb', 'repo_name': 'rafi007akhtar/intro-to-data-analysis', 'size': '9606'}