import argparse import asyncio import signal from bleak import BleakClient, BleakScanner from bleak.backends.device import BLEDevice from bleak.exc import BleakError from bleak.backends.service import BleakGATTService, BleakGATTCharacteristic, BleakGATTDescriptor from bleak.uuids import * from typing import Union import time, datetime import csv from masimo_crc8 import crc8 import struct import sys import platform import os # TODO split this up into modules name_version_str = "OxiCollect v20210825" # UUIDs for Oxiwear oxiwear_ctrl_uuid = 0x6162 # char for control register (write) oxiwear_ctrl_low_power = [1] # put device in low power (sleep) mode oxiwear_ctrl_active = [2] # activate device from low power mode oxiwear_ctrl_reset = [3] # reset the device oxiwear_data_uuid = 0x6199 # char for raw pleth data streams (notify) oxiwear_hr_uuid = 0x6198 # char for heart rate output (read | notify) oxiwear_spo2_uuid = 0x6197 # char for SpO2 output (read | notify) oxiwear_dev_name = "OxiV2" # UUIDs for Masimo masimo_rx_char_uuid = "54C21002-A720-4B4F-11E4-9FE20002A5D5".lower() masimo_tx_char_uuid = "54C21001-A720-4B4F-11E4-9FE20002A5D5".lower() masimo_magic_string = bytearray([0x77, 0x05, 0x03, 0x03, 0x00, 0x01, 0x80]) masimo_dev_name = "MightySat" def short_bt_sig_uuid_to_long(short: Union[str, int]) -> str: """ Returns the long-format UUID of a Bluetooth SIG-specified short UUID :param short: Short-form BT SIG-specified UUID (as a hex string or a raw integer) :return: The fully-qualified long-form BT SIG UUID """ hex_form = short if type(short) == str: pass elif type(short) == int: hex_form = hex(short)[2:] else: raise TypeError return f'0000{hex_form}-0000-1000-8000-00805f9b34fb' def chunkstring(string, length): return (string[0+i:length+i] for i in range(0, len(string), length)) class Collector: # If ppg_data is true, the collector will collect the raw PPG data. # Otherwise, it will collect only the readings (SpO2 and HR). # [Currently, ppg_data is supported for Oxiwear Collector only.] def __init__(self, name: str, client: BleakClient, ppg_data = False): self.name = name self.client = client self.writer = None self.outfile = None self.collect_ppg = ppg_data self.data_connected = False def create_writer(self): # Create a CSV writer that writes to a file. # file name is derived from the name member and the current date/time. # TODO: accept a custom name/path argument if needed ts = time.time() dt = datetime.datetime.fromtimestamp(ts) fname = "{}-{}{:02d}{:02d}_{:02d}{:02d}{:02d}.csv".format(self.name, dt.year, dt.month, dt.day, dt.hour,dt.minute,dt.second) self.outfile = open(fname, "w", newline='') self.writer = csv.writer(self.outfile) self.write_header() self.outfile.flush() def write_header(self): pass # default is no header; override to write one def delete_file(self): # deletes the file created by writer, if any if self.outfile: # close and delete it self.outfile.close() fn = self.outfile.name if os.path.isfile(fn): if args.verbose: print(f' deleting file "{fn}"') os.remove(fn) async def connect_to_client(self): # Does the gerneric part of connection: connecting to the client # using its MAC address. # Returns TRUE if connected, FALSE if not print(f'Connecting to {self.name} @ {self.client.address}...') connected = False try: connected = await self.client.connect(timeout=15) except Exception as e: print(f' exception: ', e) if not connected: print(f'Failed to connect to {self.name}') return connected async def connect(self): # TODO connect to targeted device raise NotImplementedError async def disconnect(self): # TODO teardown connection and prepare for thread shutdown raise NotImplementedError def __del__(self): if args.verbose: print(f'deleting {self.name}') if self.outfile: del self.outfile class OxiwearCollector(Collector): def __init__(self, name: str, client: BleakClient, ppg_data = False): super(OxiwearCollector, self).__init__(name, client, ppg_data) self.rx_char = None self.spo2_char = None self.hr_char = None self.cur_hr = 0 # HR and SpO2 come in asynchronously, so store them self.cur_spo2 = 0 # here as "current" values until we've gotten both. self.cur_count = 0 # counts number of current values we've gotten def write_header(self): if (self.collect_ppg): # header for Oxi ppg data is timestamp and 6 columns, not all of # which will contain data. self.writer.writerow(['time','count',1,2,3,4,5,6]) else: #just writing SpO2 & HR data self.writer.writerow(['time', 'spo2', 'hr']) def write_data(self, data, timestamp): # Write the data to the csv file. # Data is (up to) a 6-tuple of arrays: each array is the list of # samples from a ppg signal channel. There will be between 1 and 6 # channels, with any number of samples in each, but each channel is # assumed to contain the same number of samples in this chunk. # timestamp is added to the first row each time we're called. # count the number of columns of data in this batch. (We really don't # need to do this each time, as it seems to be constant.) num_cols = 0 for col in range(0,len(data)): if len(data[col]): num_cols += 1 if num_cols < 1: return # no data? # construct one row of values to write out. num_samp = len(data[0]) # assumes all cols have same # of samples ts_str = "{:.3f}".format(timestamp) row = [ts_str] #1st row in batch gets timestamp for i in range(0,num_samp): for j in range(0,num_cols): row.append(data[j][i]) self.writer.writerow(row) row = [''] # rest of the rows in the batch get no timestamp self.outfile.flush() def write_data_rows(self, data, timestamp): # data is arranged in rows, so just add the ts and write it ts_str = "{:.3f}".format(timestamp) out_row = [ts_str] #1st row in batch gets timestamp for row in data: out_row.extend(row) self.writer.writerow(out_row) out_row = [''] self.outfile.flush() def parse_data_rows(self, data: bytearray): # return the data in rows. Each row contains a Count field, followed # by one sample from each of the active channels: # "count, ch1, ch2, .. ch6". Count will contain a value once every 8 # rows, unless samples are missed. retval = [] row = [] global last_count # Read data in chunks of 3 bytes for chunk in chunkstring(data, 3): # Convert to a 32-bit integer word = bytearray([0]) + chunk sample = struct.unpack('>I', word)[0] tag = (sample & 0xF80000) >> 19 sample = (sample & ~0xF80000) if tag == 0x1f: if len(row) > 0: retval.append(row) row = [] last_count += 1 if sample != last_count: print(f'***MISSED samples: expected {last_count} got {sample}') last_count = sample elif tag == 1: if len(row) > 1: # we have a filled row: add it to the retval retval.append(row) row = [''] # start new row with blank count field elif len(row) == 0: row = [''] # start new row with blank count field #else, len is 1: the single value in the row is the Count elif tag > 6: # some other tag we currently don't handle print(f'*Ignored tag: {tag} {sample}') # ignore the sample (???) continue # add the sample to the row. # This assumes that the tags/channels arrive in order!! row.append(sample) if len(row) > 0: retval.append(row) return retval def parse_data(self, data: bytearray): # Seven return arrays: one for COUNT [0], and one for each LED sequence # channel [1..6]. retval = ([], [], [], [], [], [], []) global last_count # Read data in chunks of 3 bytes for chunk in chunkstring(data, 3): # Convert to a 32-bit integer word = bytearray([0]) + chunk sample = struct.unpack('>I', word)[0] tag = (sample & 0xF80000) >> 19 sample = (sample & ~0xF80000) if tag <= 6: if tag == 1: if len(retval[0]) <= len(retval[1]): retval[0].append(last_count) print(f'') print(f'{tag} ',end='') # channel 1 goes in array 1, ... retval[tag].append(sample) elif tag == 0x1f: last_count += 1 print(f'{sample} ', end='') retval[0].append(sample) if sample != last_count: print(f'***MISSED samples: expected {last_count} got {sample}') last_count = sample else: print(f'*ignored tag: {tag}') return retval def handle_data_notification(self, char_handle: int, data: bytearray): # Capture the timestamp as soon as we get the notification ts = time.time() if args.verbose: print(f'Oxiwear received data ({len(data)}): ', end='') for b in data: print(f'{hex(b)}, ', end='') print('') if self.collect_ppg: if self.rx_char.handle == char_handle: data_rows = self.parse_data_rows(data) self.write_data_rows(data_rows, ts) else: #collecting SpO2 & Hr only if self.hr_char.handle == char_handle: self.cur_hr = data[0]; self.cur_count += 1 elif self.spo2_char.handle == char_handle: self.cur_spo2 = data[0]; self.cur_count += 1 if self.cur_count == 2: # SpO2 & HR always come in pairs, though the order can vary. # Once we've gotten 2 values, we know we have both and can # write them to the file (with timestamp). ts_str = "{:.3f}".format(ts) row = [ts_str,self.cur_spo2,self.cur_hr] self.writer.writerow(row) self.outfile.flush() if args.verbose: print(f'{ts_str}, HR:{self.cur_hr}, SpO2: {self.cur_spo2}') self.cur_count = 0 # reset count to wait for next 2 values async def connect(self): if not await self.connect_to_client(): return print(f'Connected to {self.client.address}') for svc in self.client.services: if args.verbose: print(f'Service UUID: {svc.uuid}') for char in svc.characteristics: if args.verbose: print(f'\tChar UUID: {char.uuid}') # keep characteristics based on what data we're collecting: # either full ppg data or just Spo2 & HR if char.uuid == short_bt_sig_uuid_to_long(oxiwear_data_uuid): if self.collect_ppg: self.rx_char = char if args.verbose: print(f'Found Oxiwear RX Char (Handle: {char.handle})') elif char.uuid == short_bt_sig_uuid_to_long(oxiwear_spo2_uuid): if not self.collect_ppg: self.spo2_char = char if args.verbose: print(f'Found Oxiwear SpO2 Char (Handle: {char.handle})') elif char.uuid == short_bt_sig_uuid_to_long(oxiwear_hr_uuid): if not self.collect_ppg: self.hr_char = char if args.verbose: print(f'Found Oxiwear HR char (handle: {char.handle})') # make sure we got the chars we need, and register for notifications if self.collect_ppg: if not self.rx_char: print(f'error: Could not find Oxiwear RX Char') raise AssertionError await self.client.start_notify(self.rx_char, self.handle_data_notification) if args.verbose: print(f'Subscribed to RX Char') else: if (not self.spo2_char) or (not self.hr_char): print(f'error: Could not find Oxiwear SpO2 or HR Char') raise AssertionError await self.client.start_notify(self.spo2_char, self.handle_data_notification) if args.verbose: print(f'Subscribed to SpO2 Char') await self.client.start_notify(self.hr_char, self.handle_data_notification) if args.verbose: print(f'Subscribed to HR Char') print(f'{self.name}: collecting data...') self.data_connected = True async def reset_device(self): """ Reset the oxiwear device, by writing to the Control Characteristic. - connect to the device (that is, the Gatt server) - find the Control characteristic - write a '3' to it - wait some (arbitrary) amount of time... """ if not await self.connect_to_client(): return ctrl_char = None print(f'Connected to {self.client.address}') for svc in self.client.services: if args.verbose: print(f'Service UUID: {svc.uuid}') for char in svc.characteristics: if args.verbose: print(f'\tChar UUID: {char.uuid}') if char.uuid == short_bt_sig_uuid_to_long(oxiwear_ctrl_uuid): ctrl_char = char if args.verbose: print(f'Found Oxiwear Ctrl Char (Handle: {char.handle})') if ctrl_char != None: print(f'Resetting device...') await self.client.write_gatt_char(ctrl_char, oxiwear_ctrl_reset) await asyncio.sleep(1) print(f' Reset done.') async def disconnect(self): if args.verbose: print(f'OxiWear disconnect') if await self.client.is_connected(): if self.rx_char: await self.client.stop_notify(self.rx_char) if self.spo2_char: await self.client.stop_notify(self.spo2_char) if self.hr_char: await self.client.stop_notify(self.hr_char) if args.verbose: print(f' disconnecting...') await self.client.disconnect() print(f'{self.name} disconnected.') class MasimoCollector(Collector): def __init__(self, name: str, client: BleakClient): super(MasimoCollector, self).__init__(name, client) self.rx_char = None self.tx_char = None self.buffer = bytearray([]) def write_header(self): self.writer.writerow(['time', 'spo2', 'hr']) def parse_data(self, data: bytearray): parsed_data = {"pleth_wave_points": [], "sigIq_wave_points": [], "spo2_val": None, "hr_val": None} # Check SOM byte if data[0] != 0x77: print(f"{self.name}: error parsing data - incorrect SOM byte") return None # Collect the payload payload = data[2:-1] # Check the CRC if data[-1] != crc8(payload, 0, len(payload)): print(f"{self.name}: invalid crc8 in data") return None # Handle different data types # Payload type: Waveform data if payload[0] == 4: for i, b in enumerate(payload[2:]): if i % 2 == 0: # Even index = pleth data point parsed_data["pleth_wave_points"].append((float(b) / 128.0) * 32767.0) else: parsed_data["sigIq_wave_points"].append(float(b) / 100.0) # Payload type: data point (SpO2, HR, Perfusion Index, Respiratory Rate, Pleth Variability) elif payload[0] == 5: # TODO - this will need to be updated if we collect more than just SpO2 and HR values parsed_data["spo2_val"] = payload[6] parsed_data["hr_val"] = payload[8] if args.verbose: print("SpO2: {}, HR: {}".format(payload[6], payload[8])) self.writer.writerow([time.time(), payload[6], payload[8]]) self.outfile.flush() return parsed_data def handle_data_notification(self, char_handle: int, data: bytearray): if args.verbose: print(f'Masimo recieved data ({len(data)}): ', end='') for b in data: print(f'{hex(b)}, ', end='') print('') self.buffer += data # Find all instances of the SOM byte (0x77) that delineate messages and parse each # Buffering is necessary because sometimes a complete message does not come in all at once due to MTU soms = [i for i, x in enumerate(self.buffer) if x == 0x77] previous_som = 0 msgs = [] for som in soms: msgs.append(self.buffer[previous_som:som]) previous_som = som for msg in msgs: if len(msg): self.parse_data(msg) # Remove data that has been processed self.buffer = self.buffer[soms[-1]:] async def connect(self): if not await self.connect_to_client(): raise AssertionError print(f'Connected to {self.client.address}') for svc in self.client.services: if args.verbose: print(f'Service UUID: {svc.uuid}') for char in svc.characteristics: if args.verbose: print(f'\tChar UUID: {char.uuid}') if char.uuid == masimo_rx_char_uuid: self.rx_char = char if args.verbose: print(f'Found Masimo RX Char (Handle: {char.handle})') elif char.uuid == masimo_tx_char_uuid: self.tx_char = char if args.verbose: print(f'Found Masimo TX Char (Handle: {char.handle})') if not self.rx_char: print(f'error: Could not find Masimo RX Char') raise AssertionError if not self.tx_char: print(f'error: Could not find Masimo TX Char') raise AssertionError await self.client.start_notify(self.rx_char, self.handle_data_notification) if args.verbose: print(f'Subscribed to RX Char') # Now we write the magic string to the TX characteristic to enable streams # Note: I reverse-engineered/decompiled the Android Java Masimo app to figure out how to # interact with the sensor. There are additional streams and other data that can be enabled by modifying # this magic string await self.client.write_gatt_char(self.tx_char, masimo_magic_string) print(f'{self.name}: collecting data...') self.data_connected = True async def disconnect(self): if args.verbose: print(f'Masimo disconnect()') if await self.client.is_connected(): if self.rx_char: if args.verbose: print(f' stopping notification') await self.client.stop_notify(self.rx_char) if args.verbose: print(f' disconnecting') await self.client.disconnect() print(f'{self.name} disconnected.') async def scan_for_devices(): """ Do a BLE discover to find all nearby devices. Prints all devices found, and returns the entire list. """ ble_timeout = 10 print(f'Discovering BLE devices ({ble_timeout} seconds)...') devices_found = await BleakScanner.discover(ble_timeout) for device in devices_found: print(' ', device.address, device.name) return devices_found def create_client(spec, name, devices): """ Create a Bleak Client based on the contents of spec. If spec is a device (MAC) address, create the client using that. (In this case, name and devices are ignored.) If spec is "scan", then look for a device in the devices list that matches name. Returns a BleakClient, or None if not found. """ client = None if spec == None: return None elif spec == 'scan': # see if there's a device in the list with the given name for dev in devices: if dev.name == name: print(f'{name} found.') client = BleakClient(dev) if client == None: print(f'*** {name} not found.') else: # use oxi mac address specified on cmd line # TODO: verify it's a valid address format... client = BleakClient(spec) return client async def terminate_collection(collectors, delete_files=False): print(f'Terminating collection...') for collector in collectors: if args.verbose: print(f'deleting collector {collector.name}') await collector.disconnect() if delete_files: collector.delete_file() del collector async def shutdown(sig, async_loop): """Cleanup tasks tied to the service's shutdown.""" if args.verbose: print(f'Received exit signal {sig.name}...') print(f'Number of total tasks: {len(asyncio.all_tasks())}') tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()] if args.verbose: print(f'Cancelling {len(tasks)} outstanding tasks') [task.cancel() for task in tasks] await asyncio.gather(*tasks, return_exceptions=True) async_loop.stop() def sig_handler(sig, frame): """Cleanup at the application level.""" if args.verbose: print(f'Exit Signal received: {sig}!') global keep_running if not keep_running: # if this is our 2nd try, then just quit now print(f'Quitting application.') sys.exit() keep_running = False #TODO: wait for everything to end, then exit()? # Nope, can't. Cant await in signal handler because it is not 'async' # Can't make it async because then it doesn't actually get called on the # signal event. async def main(): collectors = [] devices = [] # global flag to tell everyone when it's time to quit global keep_running keep_running = True global last_count last_count = 0 # if we need to, do a BLE scan for nearby devices if args.scan_only or args.oxi_mac == 'scan' or args.masimo_mac == 'scan' or args.reset_mac == 'scan': devices = await scan_for_devices() if args.scan_only: # we're done! asyncio.get_running_loop().stop() return # first handle the 'reset device' case if args.reset_mac != None: reset_client = create_client(args.reset_mac, oxiwear_dev_name, devices) if reset_client != None: oxi_collector = OxiwearCollector("oxiwear", reset_client) await oxi_collector.reset_device() await oxi_collector.disconnect() del oxi_collector # if we're resetting the device, that's all we do. asyncio.get_running_loop().stop() return else: not_found = True # create Bleak Clients for the devices we need not_found = False oxi_client = None if args.oxi_mac != None: oxi_client = create_client(args.oxi_mac, oxiwear_dev_name, devices) if oxi_client == None: not_found = True masimo_client = None if args.masimo_mac != None: masimo_client = create_client(args.masimo_mac, masimo_dev_name, devices) if masimo_client == None: not_found = True # Do we have anything to do? if not_found or (not oxi_client and not masimo_client): print(f'One or more devices NOT found - stopping application.') asyncio.get_running_loop().stop() return # Kick off collector threads for the Oxiwear and Masimo devices not_found = False if oxi_client and oxi_client.address: try: # TODO allow multiple masimo/oxiwear devices simultaneously # TODO add stream handler output too? # TODO make logging.Handler that will plot the data? oxiwear_collector = OxiwearCollector(f"oxiwear", oxi_client, args.ppg_data) oxiwear_collector.create_writer() await oxiwear_collector.connect() collectors.append(oxiwear_collector) except Exception as e: print(f'Error connecting to Oxiwear: {e}') if not oxiwear_collector.data_connected: oxiwear_collector.delete_file() not_found = True if masimo_client and masimo_client.address: try: if args.verbose: print(f'creating collector') # TODO: pass args.ppg_data to Masimo constructor once it is supported. masimo_collector = MasimoCollector(f"masimo", masimo_client) if args.verbose: print(f' collector created!') masimo_collector.create_writer() await masimo_collector.connect() collectors.append(masimo_collector) except Exception as e: print(f'Error connecting to Masimo: {e}') if not masimo_collector.data_connected: masimo_collector.delete_file() not_found = True if not_found or not len(collectors): print(f'One or more connections FAILED - stopping application.') if len(collectors): await terminate_collection(collectors, delete_files=True) asyncio.get_running_loop().stop() return dots = 0 while keep_running: try: # output dots periodically to indicate collection is in progress dots += 1 print(f'.', end='', flush=True) if dots >= 59: dots = 0 print(f'') await asyncio.sleep(1) except asyncio.CancelledError: print(f'CancelledError - main loop') break # cleanup print() await terminate_collection(collectors) # stop the loop if args.verbose: print(f'stopping main task loop...') asyncio.get_running_loop().stop() if args.verbose: print(f'end of main task') if __name__ == '__main__': print(name_version_str) parser = argparse.ArgumentParser(description='Wirelessly connect to a nearby OxiWear ' 'or Masimo MightySat device and collect data.') parser.add_argument('-s', '--scan-only', dest='scan_only', action='store_true', help='Perform a BLE scan only, and list devices found. (-o and -m options are ignored.)') # nargs='?': One argument will be consumed from the command line if possible. # If no command-line argument is present, the value from default will be produced. # If the option string is present but not followed by a command-line argument, # the value from const will be produced. parser.add_argument('-o', '--oxiwear', nargs='?', const='scan', default=None, dest='oxi_mac', help='OxiWear device address to connect to (in hex, colon separated).' ' If not present no OxiWear will be connected to.' ' If flag present with no address, a scan for OxiWear will be performed.') parser.add_argument('-m', '--masimo', nargs='?', const='scan', default=None, dest='masimo_mac', help='MAC address of Masimo to connect to.' ' If not present, no Masimo will be connected to.' ' If flag present with no address, a scan will be performed' ' and the first Masimo found will be connected to.') parser.add_argument('-r', '--reset', nargs='?', const='scan', default=None, dest='reset_mac', help='Reset the OxiWear device. (-o and -m options are ignored.)' ' If flag present with no address, a scan for OxiWear will be performed.') parser.add_argument('-p', '--ppg-data', dest='ppg_data', action='store_true', help='Collect PPG waveform data. If not present, collects SpO2 & HR values instead.' ' (Supported by Oxiwear only - ignored otherwise.)') parser.add_argument('-v', dest='verbose', action='store_true', help='Increase verbosity of output') args = parser.parse_args() if args.verbose: print(args) #if the user didn't specify anything to do, print usage if (not args.scan_only) and (not args.oxi_mac) and (not args.masimo_mac) and (not args.reset_mac): parser.print_help() sys.exit(0) loop = asyncio.get_event_loop() # Gracefully shutdown with signal handlers if platform.system() == 'Windows': # Windows-supported signals signals = (signal.SIGBREAK, #ctrl-break (windows-only) signal.SIGTERM, signal.SIGABRT, signal.SIGINT #ctrl-c ) else: # assume linux # Linux signals signals = (signal.SIGHUP, signal.SIGTERM, signal.SIGINT) for s in signals: # Not all systems (eg, Windows) support adding a signal handler to an # event loop, so add it to the main thread instead. signal.signal(s, sig_handler) try: main_task = loop.create_task(main()) loop.run_until_complete(main_task) if args.verbose: print(f'main_task is complete') except Exception as e: print(f'__main__ exception: {e}') finally: if loop.is_running(): if args.verbose: print(f'stopping loop.') loop.stop() if not loop.is_closed(): if args.verbose: print(f'closing loop') loop.close()
Write, Run & Share Python code online using OneCompiler's Python online compiler for free. It's one of the robust, feature-rich online compilers for python language, supporting both the versions which are Python 3 and Python 2.7. Getting started with the OneCompiler's Python editor is easy and fast. The editor shows sample boilerplate code when you choose language as Python or Python2 and start coding.
OneCompiler's python online editor supports stdin and users can give inputs to programs using the STDIN textbox under the I/O tab. Following is a sample python program which takes name as input and print your name with hello.
import sys
name = sys.stdin.readline()
print("Hello "+ name)
Python is a very popular general-purpose programming language which was created by Guido van Rossum, and released in 1991. It is very popular for web development and you can build almost anything like mobile apps, web apps, tools, data analytics, machine learning etc. It is designed to be simple and easy like english language. It's is highly productive and efficient making it a very popular language.
When ever you want to perform a set of operations based on a condition IF-ELSE is used.
if conditional-expression
#code
elif conditional-expression
#code
else:
#code
Indentation is very important in Python, make sure the indentation is followed correctly
For loop is used to iterate over arrays(list, tuple, set, dictionary) or strings.
mylist=("Iphone","Pixel","Samsung")
for i in mylist:
print(i)
While is also used to iterate a set of statements based on a condition. Usually while is preferred when number of iterations are not known in advance.
while condition
#code
There are four types of collections in Python.
List is a collection which is ordered and can be changed. Lists are specified in square brackets.
mylist=["iPhone","Pixel","Samsung"]
print(mylist)
Tuple is a collection which is ordered and can not be changed. Tuples are specified in round brackets.
myTuple=("iPhone","Pixel","Samsung")
print(myTuple)
Below throws an error if you assign another value to tuple again.
myTuple=("iPhone","Pixel","Samsung")
print(myTuple)
myTuple[1]="onePlus"
print(myTuple)
Set is a collection which is unordered and unindexed. Sets are specified in curly brackets.
myset = {"iPhone","Pixel","Samsung"}
print(myset)
Dictionary is a collection of key value pairs which is unordered, can be changed, and indexed. They are written in curly brackets with key - value pairs.
mydict = {
"brand" :"iPhone",
"model": "iPhone 11"
}
print(mydict)
Following are the libraries supported by OneCompiler's Python compiler
Name | Description |
---|---|
NumPy | NumPy python library helps users to work on arrays with ease |
SciPy | SciPy is a scientific computation library which depends on NumPy for convenient and fast N-dimensional array manipulation |
SKLearn/Scikit-learn | Scikit-learn or Scikit-learn is the most useful library for machine learning in Python |
Pandas | Pandas is the most efficient Python library for data manipulation and analysis |
DOcplex | DOcplex is IBM Decision Optimization CPLEX Modeling for Python, is a library composed of Mathematical Programming Modeling and Constraint Programming Modeling |