# data[].attributes.body
import pylab as plt
allows me to reference any library procedure as plt.<procname>

e.g plt.plot(x, y) (x and y could be lists and should have the same length)
if I do a lot of plots they are all shown in the same figure :(
to have a different figure for each plot then: plt.figure('Name')
to label the axis of the plot then: plt.xlabel('Name') , plt.ylabel('Name')
to put a title to the plot: plt.title('Name')
plt.clf() to clear and redraw a plot

How to visualize the differences in the graphs when they have different scales?

1) set limits on the values of the axis
e.g. plt.ylimit(0,1000)
2) plot multiple function in the same display = ovelaying plots
to add a legend that identifies each plot
plt.plot(x, y, label='Name')
plt.legend()  or plt.legend(loc='upper left')
to change the color and the line of the graph I add an argument in the plot:
plt.plot(x, y, 'argument', linewidth=5 ) the argument can contain the color: r,g,b,k and the line type:-,o,--,^ and linewidth determines how thick the line wiil be
e.g: plt.plt(x, y, 'ro', linewidth=5)

# data[].attributes.attachment_link.title
6.00.1x Introduction to Computer Science Using Python

# data[].attributes.attachment_link.description
Saved coding session broadcasts for the edX course 6.00.1x.

# data[].attributes.body
bogo sort, monkey sort, stupid sort
randomly assign elements, check if they are sorted and repeat till they are sorted!
Complexity is O(infinity), best case O(len(L))
def bogo_sort(L):
while not is_sorted(L):
random.shuffle(L)
print(L)
return L

Bubble sort
Complexity is quadratic O(n^2) because in the worst case I have len(L)-1 comparisons for the external loop and len(L)-1 repetitions for the internal loop
def bubble_sort(L):
swap=False
while not swap:
swap=True
for j in range(1,len(L)):
if L[j-1]>L[j]:
swap=False
temp=L[j]
L[j]=L[j-1]
L[j-1]=temp
return L

Selection sort
Complexity is quadratic O(n^2) because in the worst case I have len(L) repetitions  for the external loop and len(L)-suffixSt repetitions for the internal loop
def selection_sort(L):
suffixSt=0
while suffixSt!=len(L):
for i in range(suffixSt, len(L)):
if L[i]<L[suffixSt]:
L[suffixSt],L[i]=L[i], L[suffixSt]
suffixSt+=1
return L

Merge Sort - Divide an conquer approach
Complexity for merging  is linear O(n) . O(len(sublist1)+len(sublist2))=O(len(L))
# left and right sublists are sorted
Complexity for sorting is logarithmic because  I devide the list in halfs and I "cut" the problem in half by the recursive calls. O(log(n)) , O(log(len(L))
So overall complexity is O(n log(n)) specifically O(len(L) log((len(L))
def merge(left,right):
result=[]
i,j=0,0
while i<len(left) and j<len(right):
if left[i]<right[j]:
result.append(left[i])
i+=1
else:
result.append(right[j])
j+=1
while i<len(left):
result.append(left[i])
i+=1
while j<len(right):
result.append(right[j])
j+=1
print(result)
def merge_sort(L):
if len(L)<2:
return L[:]
else:
middle=len(L)//2
left=merge_sort(L[:middle])
right=merge_sort(L[middle:])
return merge(left,right)

# data[].attributes.body
Linear Search (force brute search) -
Complexity linear O(n) ,  O(len(L))
def Linear_Search(L, x):
found=False
for i to range(len(L)):
if x==L[i]:
found=True
return found

Why bother sorting before searching?
sort the list first in order to perform a lot of searches
O(sort + K* log(len(L)) < K * O(len(L))
for large K the time to sort is irrelevant

Bisection Search (list must be sorted)
Complexity linear O(n) ,  O(len(L))
def Bisection_Search(L, x)
for i to range(len(L)):
if x==L[i]:
return True
elif x>L[i]:
return False
return False

Bisection Search  - logarithmic complexity O(log(len(L)) because it used indexes to cut the list in half each time
def bisect_Search2(L, x):
def bisect_Search_helper(L, x, start, end):
if start==end:
return L[start]==x
middle=(start+end)//2
if L[middle]==x:
return True
elif L[middle]>x:
if start==middle:
return False
else:
return bisect_Search_helper(L, x, start, middle-1)
else:
return bisect_Search_helper(L, x, middle+1, end)
if L==[]:
return False
else:
return bisect_Search_helper(L, x, 0, len(L)-1)

Bisection Search  - logarithmic complexity O(n log(len(L))
because it is logarithmic for each recursive call and makes a copy of the list
def bisect_Search1(L, x):
if L==[]:
return False
elif len(L)==1:
return L[0]==x
else:
middle=len(L)//2
if L[middle]>x:
return bisect_Search1(L[:middle], x)
else:
return bisect_Search1(L[middle:], x)

# data[].attributes.attachment_link.readable_text
John Guttag. 6.00SC Introduction to Computer Science and Programming. Spring 2011. Massachusetts Institute of Technology: MIT OpenCourseWare, https://ocw.mit.edu. License: Creative Commons BY-NC-SA.

For more information about using these materials and the Creative Commons license, see our Terms of Use.

# data[].attributes.attachment_link.title
Introduction to Computer Science and Programming

# data[].attributes.attachment_link.description
This subject is aimed at students with little or no programming experience. It aims to provide students with an understanding of the role computation can play in solving problems. It also aims to help students, regardless of their major, to feel justifiably confident of their ability to write small programs that allow them to accomplish useful goals.

# data[].attributes.body
O(1) constant complexity
O(log2(n)) logarithmic complexity when it grows as the logarithm of the size of one of its inputs E.g. the search is divided in half in a bisection search
O(n) Linear complexity e.g sequential search and all the iterations that include constant operations.
O(nlog2(n)) Log-Linear complexity e.g merge sort
O(n^c) Polynomial complexity - most common is Quadratic O(n^2) that grows with the square of the size of the input, usually when we have nested loops or recursive function calls.
O(c^n) Exponential complexity - recursive functions with more than one recursive call for each of the problem e.g towers of hanoi

# data[].attributes.body
To combine complexity classes:
- Law of addition O(f(n))+O(g(n))=
O(f(n)+g(n)) e.g. 0(n)+O(n^2)=O(n^2)
- Law of multiplication O(f(n))*O(g(n))=
O(f(n)*g(n))  e.g. O(n)*O(n)=O(n*n)=O(n^2)

# data[].attributes.headline
Measuring order of Growth - Big O Notation

# data[].attributes.subject
Measuring order of Growth - Big O Notation

# data[].attributes.body
Measures an upper bound on the asymptotic growth of complexity.
Asymptotic means as the problem input size gets really big what is the behavior? and how do I put an upper bound which will let me describe that class of behavior?
Big O is used to describe the worst case.
E.g for the computation of the factorial:
def fact_iter(n):
answer=1
while n>1:
answer*=n
n-=1
return answer
I calculate the number of steps to be: 1+5*n+1
I can ignore the additive constants, the two ones. The fact that it's 5n as opposed to 7n or 3n, as n gets large really also doesn't matter. Practically, it'll be a little bit difference in time.But as an asymptotic behavior, it really doesn't matter.
So... I say that this a linear or order n.  O(n)
I look at the dominant value to determine the order e.g
n^2+2n+1  O(n^2)
n^2+1000n+1   O(n^2)
log(n)+n+4    O(n)
0.01*n*log(n)+500   O(nlog(n))
2n^30+3^n     O(3^n)

# data[].attributes.body
fast computer fast VS large datasets
Efficiency of time and of space
How to evaluate efficiency?
1) measure with timer
2) count the operations
3) abstract notion of order of growth
TIMING A PROGRAM (example)
import time
def celcius_farhenait(c):
return c*9/5+32
t0=time.clock()
celcius_farhenait(40)
t1=time.clock()
print(t1-t0)
BUT timing programs is incosistent
COUNTING OPERATIONS OF A PROGRAM (example)
-  mathematical operations
- comparisons
- assignement
- assessing objects in memory
ORDERS OF GROWTH TO EVALUATE THE EFFICENCY OF THE PROGRAM
- check for very big input
- growth of program's runtime as input gets bigger
- put an upper bound on growth
- express "order of growth" not "exact"

# data[].attributes.body
def genFib():
fibn_1=1
fibn_2=0
while True:
next=fibn_1+fibn_2
yield next
fibn_2=fibn_1
fibn_1=next
to test it:
f=genFib()
for i in range (5):
print(f.__next__())

I can use for returning one by one value

# data[].attributes.body
Instance variables are:
- specific to an instance
- created for an istance/belong to an instance
- use the generic variable name self
Class Variables are:
- belong to the class
- defined inside the class /outside its methods
- shared among objects/instances of that class

class Animal(object):
def __init__(self, age):
self.age=age
self.name=None
class rabbit(Animal):
tag=1
def __init__(self, age, parent1=None, parent2=None):
Animal.__init__(self, age)
self.parent1=parent1
self.parent2=parent2
self.rid=rabbit.tag
rabbit.tag+=1
def get_rid(self):
return str(self.rid).zfill(3)
def get_parent1(self):
return self.parent1
def get_parent2(self):
return self.parent2

# data[].attributes.body
I build classes that reflect hierarchy, i.e. sharing the properties and the methods that a re common, but having distinct properties, attributes, methods when appropriate.
parent class (superclass)
doesnot have access to data and methods of its subclasses
child class (subclass) inherits all data and behavor from its parent class but can also:
- add info    - add behavior    - override behavior

class Animal(object):
def __init__(self, age):
self.age=age
self.name=None
class cat(Animal):
# add new functionality with speak()
def speak(self):
print("meau")
def __str__(self):
return "cat: "+str(self.name)
class person(Animal):
def __init__(self, name, age):
Animal.__init__(self, age)
Animal.set_name(self, name)
self.friends=[]
def get_friends(self):
return self.friends
def add_friend(self, fname):
if fname not in self.friends:
self.friends.append(fname)
def speak(self)        :
print("hello guys")
class student(person):
def __init__(self, name, age, major=None):
person.__init__(self, name, age)
self.major=major
def change_major(self, major):
self.major=major

# data[].attributes.body
- bundle together objects that share some common properties:

1. they share common attributes,

2. they share procedures that operate on those attributes.

- abstraction = make a distinction between how to implement the object and how to use it.
- I don't have to think about the internal representation. I don't have to think about the details of how to get at it. I simply know that there are a set of methods associated with it. => I build layers of abstractions that are going to inherit from other classes of objects.
-  I build my own class of objects on top of Python objects.

# data[].attributes.attachment_link.title
class definition vs instance of a class.JPG

# data[].attributes.body
class Coordinate(object):
def __init__(self, x, y):
self.x=x
self.y=y
def __sub__(self, other):
return Coordinate(self.x-other.x, self.y-other.y)
c=Coordinate(3,4)
b=Coordinate(0,
print (c-b)

# data[].attributes.body
type(c) to check the type of an object instance
isinstance(Coordinate,c) to check if c is an object of the class Coordinate

# data[].attributes.body
class Coordinate(object):
def __init__(self, x, y):
self.x=x
self.y=y
def __str__(self):
return "<"+str(self.x)+","+str(self.y)+">"

# data[].attributes.body
c=Coordinate(3,4)
b=Coordinate(0,0)
print(c.distance(b))

alternative way:
c=Coordinate(3,4)
b=Coordinate(0,0)
print(Coordinate.distance(c,b))

# data[].attributes.body
a method is like a function but it only works in the class
class Coordinate(object):
def distance(self, other):
x_diff_sq=(self.x-other.x)**2
y_diff_sq=(self.y-other.y)**2
return (x_diff_sq + y_diff_sq)**0.5

# data[].attributes.body
class Coordinate(object):
def __init__(self, x, y):
self.x=x
self.y=y
έτσι στην συνέχεια έχω στον compiler:
c=Coordinate(3,4)
b=Coordinate(0,0)
print(c.x) μου δίνει 3
print(b.x) μου δίνει 0
Δηλαδή μου δίνει το instance των δεδομένων που είχα κάθε φορά.

# data[].attributes.body
class name (object):
<attributes>
Attibutes can be:
-  data and procedures that "belong" to the class.
- data attributes
- procedural attributes (methods

# data[].attributes.body
L[i], L[i:j], L[i,j,k], +
len(), max(), min(), del(l[i])
L.append(),  L.extend(),  L.count(), L.index()
L.insert(), L.pop(),  L.remove(), L.sort(),
L.reverse()

# data[].attributes.body
Each object is a data abstraction. It captures an internal representation of the data attributes and it defines an interface for interacting with the object through methods.

# data[].attributes.body
All the primitiveobjects have:
1. a type
2. an internal data representation.
3. associated with it, it has a set of procedures that allow you to interact with an instance of that object.

# data[].attributes.body
For good defensive programming.
I do an assertion that the following should be true, and if it's not, raise an exception. e.g
def avg(grades):
assert not len(grades)==0, 'no data'
return sum(grades)/len(grades)
Use assertions to check:
1. types of arguments or values
2. that invariants on data structure are met
3. constrains on return values
4. violations of constrains on procedures

# data[].attributes.body
raise <exception_name> (<arguments>

# data[].attributes.body
except ValueError:
except IOError:
except ZeroDivisionError:

# data[].attributes.body
try:
code I want to try
except:
print ('error')

else:
finally:

# data[].attributes.body
A path-complete glass box test suite would find test cases that go through every possible path in the code. This is different from black-box testing, because in black-box testing you only have the function specification. For glass-box testing, you actually know how the function you are testing is defined. Thus you can use this definition to figure out how many different paths through the code exist, and then pick a test suite based on that knowledge.

# data[].attributes.body
A method of software testing that tests the functionality of an application. A way to think about black-box testing is to look at both:

The possible paths through the specification.

The possible boundary cases.

# data[].attributes.body
1) design your code so that it easily supports testing and debugging. E.g. break the program up into simple modules, self-contained modules, that can be easily tested and debugged individually.
2) Write document constraints on the modules for e.g. expected input/output.
3) document the assumptions about code design. (thinking process when created this code)

# data[].attributes.body
How do I test my code?And when it doesn't work the way I expected it to, how do I go about debugging it?

# data[].attributes.body
Data structure that stores pair of data.
key:value  key must be unique and of  immutable type (int,float,string,tuple,bool)
d{} to declare, e.g.
d={'Ana':12, "Bill":19}
'chris' in D   to check
d['sylvie']=17 to add entry
del (d['john']) to delete an entry
d.keys() to get the keys
d.values() to get the values
len(d['Ana']) to get the length of its value

# data[].attributes.body
E.g. Apply a function to each element of a list
def applyelem(L, f):
for i in range(len(L)):
L[i]=f(L[i])
L=[1, 4, -9, -3]
applyelem(L, abs)
E.g. Apply functions as lists
def applyfun(L, x):
for i in L
print (i(x))
L=[abs, int]
applyfun(L, -9.0

# data[].attributes.body
Everything in Python, or almost everything in Python, is an object.

Objects have data inside of them but they also have methods and functions. And we get to that by using that dot.
object.dosomething()
E.g. L.append['f']

# data[].attributes.body
A list is an ordered sequence of information, accessible by an index.
A list is mutable. I can go in and change the element of a list to be something different.
L[] to declare
L=(1, 'two', 3)
L[0] to refer to the value of the 1st element
len(L)
L[1]=5  to change value

L.append[3] to add (only) one element
L.extend([6,9])
del(L[2]) to delete element at a specific index
L.pop() remove last element and return it
L.pop(2) remove 3rd element and return it
L.remove(2) remove the 1st instance of the specific element (return error if the element is not in the list)
list(stringname) to convert a string to a list
s.split(5) convert a string to a list with two elements <before and after the chosen one)
"".join(L) creates a string by joining the elements of a list.
L.sort() to sort a list and change it
sorted[L] to get a new shorted version of the list'
L.reverse() to reverse a list and change it
L.index(1) finds where it first occurs
listA.count(4) counts how many times the element 4 occurs in the list
L2=L creates the alias L2 copying elements form L. Alias L2 changes when L changes
L3=L[:] creates the clone L3 copying elements form L. Clone L3 doesn't change according to L
L2==L3 checks elements , L2 is L3 checks identity to see if it is alias

# data[].attributes.body
A tuple is an ordered sequence of elements which can include any different kind of element within them.
A tuple is not mutable. I can't change the values of its elements.
t=()  to declare
t=(1, 'two', 3)
t[1]  use index to refer to the value
t=t+('four', 5)
They are immutable. I cannot do e.g t[2]=3
t[1:2] gives ('two',) the extra comma means a tuple with one element
USING TUPLES
- swap (x,y)=(y,x)
- return more results from functions, return(x,y)

# data[].attributes.body
namehandle=open('nametest','w')
for i in range(5):
name=input('Give the name ')
namehandle.write(name + '\n')
namehandle.close()
namehandle=open('nametest','r')
for line in namehandle:
print (line)
namehandle.close()

# data[].attributes.body
a Python file that has something with a .py extension to it that's a collection of Python definitions of statements.
To use it:
import modulename
print(modulename.x)
print(modulename,def(3))
If I know that the names in that module don't collide with things then:
from modulename import *

# data[].attributes.body
If I have one or more base cases that are really easy to solve, and can be solved directly, then I can solve a problem by reducing it to a solution to a simpler version of the same problem until I get down to a simple base case.

# data[].attributes.body
The convention for object methods is to use the "dot" notation, so that if s is a string, evaluating s.upper will return the actual function, and evaluating s.upper() will cause the function itself to be evaluated (in this case it returns a new string, since strings are immutable) with every character now in upper case.
EXAMPLES
s.upper() s.capitalize()
s.lower() swapcase()
s.isupper() s.islower()
s.index('e') s.find('n')
s.count('e')
s.replace('e', '*')

# data[].attributes.body
Used to encapsulate pieces of computation.
Gives 2 important aspects of computational thinking: 1. decomposition (modularity) = break the problem into small, self-contained pieces
2. abstraction - suppress  details of method to compute sth, just use it
Ways to call a function
e.g. for f(x,y,z)
f(5,3,True)
f(x=5,y=3,z=True)
f(z=False, x=5, y=3) I can change order if I specify the name of the parameter
e.g. for f(x,y,z=True) I can call it f(5,3)

# data[].attributes.body
due to their conversion in binary it is better to compare:
abs(x-y)<small number
than x==y

# data[].attributes.body
n=0
while n < 5:
print(n)
n = n + 1
for n in range (5):
print(n)
** range(5) gives 0..4
for n in range (1,5,2)
** start:end+1:step
sum = 0
for n in range(1,5):
sum = sum + n
ή sum +=n
print (n, sum)
break get out of the loop
for char in s: (for strings)

# data[].attributes.body
print(x)
print("number",x)
print("number"+x)
x=input()
x=input("enter sth")
***input returns a string so for other types we do:
=int(input('give number'))

# data[].attributes.body
x="helen"
len(x)
x[0] , x[1:3], x[:3], x[1:], x[:]
x[:-1] , χ[0:4:2] , χ[::-1]
'hel' in x
print(s[::-1]) for reverse
strings are immutable
s='x'+s[1:len(s)] e.g. to change the 1st letter

# data[].attributes.body
int
float
bool
nonetype
str

# data[].attributes.body
1. **(^)
2. *,  /, //(div), %(mod)
3. +, -
left to right

# data[].attributes.body
Semantics is the meaning associated with that syntactically correct string of symbols with no semantic errors.
Syntax = Determines whether a string is legal
Semantics = Assigns a meaning to a legal sentence
Static semantics = Determines whether a string has meaning

# data[].attributes.body
Infinite loops in your programs

If you accidentally created an infinite loop and your program sits for a few seconds without printing anything out or terminating, restart the console.
You can do this in Spyder by hitting Ctrl + c (Ctrl key and the c button on your keyboard) in the console. Or go to the button beside the red square at the top of your console and select Restart Kernel. If this doesn't work, restart Anaconda/Spyder.
You can do this in IDLE by clicking Ctrl + F6 or by going to the menu Shell -> Restart Shell

# data[].attributes.body
In [1]: 16**0.5
Out[1]: 4.0
In [2]: import math
In [3]: math.sqrt(16)
Out[3]: 4.0

# data[].attributes.body
Code Editor that lets you create and edit existing Python source files

IPython interpreter pane, which gives you access to the Python interactive mode

# data[].attributes.body
Tools -> preferences
"Clear variables before execution”
To work with plots, you will need these Python library packages: matplotlib and numpy. ->
Start Spyder and type “import numpy” and “import matplotlib” into the prompts.

# data[].attributes.body
https://www.anaconda.com/distribution/

# data[].attributes.attachment_link.readable_text
We're sorry this site works best with Javascript enabled browsers.
Forms and other actions may not work properly.

Individual Edition

With over 20 million users worldwide, the open-source Individual Edition (Distribution) is the easiest way to perform Python/R data science and machine learning on a single machine. Developed for solo practitioners, it is the toolkit that equips you to work with thousands of open-source packages and libraries.

Anaconda Individual Edition is the world's most popular Python distribution platform with over 20 million users worldwide. You can trust in our long-term commitment to supporting the Anaconda open-source ecosystem, the platform of choice for Python data science.

Search our cloud-based repository to find and install over 7,500 data science and machine learning packages. With the conda-install command, you can start using thousands of open-source Conda, R, Python and many other packages.

Individual Edition is an open source, flexible solution that provides the utilities to build, distribute, install, update, and manage software in a cross-platform manner. Conda makes it easy to manage multiple data environments that can be maintained and run separately without interference from each other.

Build machine learning models

Build and train machine learning models using the best Python packages built by the open-source community, including scikit-learn, TensorFlow, and PyTorch.

Get Started

The open-source community at your fingertips

With Anaconda Individual Edition, the open-source world is your oyster. From robotics to data visualization, you can access the open-source software you need for projects in any field.

Open Source

User interface makes learning easier

Anaconda Navigator is a desktop GUI that comes with Anaconda Individual Edition. It makes it easy to launch applications and manage packages and environments without using command-line commands.

Expedite your data science journey with easy access to training materials, documentation, and community resources including Anaconda.org.

Install Anaconda

Anaconda for the enterprise

With Anaconda's Team and Enterprise Editions, our stack can handle the most advanced enterprise data science requirements.

See Products

What is Anaconda used for?

Getting started with Anaconda

Expedite your data science journey with easy access to training materials, documentation, and community resources including Anaconda Cloud.

Anaconda Installers

Supercharge your data science efforts with Anaconda .
Get Started

This website uses cookies to ensure you get the best experience on our website. Privacy Policy

# data[].attributes.attachment_link.description
Anaconda's open-source Individual Edition is the easiest way to perform Python/R data science and machine learning on a single machine.