<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Query on Digi Hunch</title><link>https://static.digihunch.com/tag/query/</link><description>Recent content in Query on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Sat, 20 Jul 2024 18:37:28 -0400</lastBuildDate><atom:link href="https://static.digihunch.com/tag/query/index.xml" rel="self" type="application/rss+xml"/><item><title>How imaging devices talk to each other (in DICOM)</title><link>https://static.digihunch.com/2020/11/how-imaging-devices-talk-to-each-other-tip-in-dicom/</link><pubDate>Sun, 15 Nov 2020 18:40:00 -0400</pubDate><guid>https://static.digihunch.com/2020/11/how-imaging-devices-talk-to-each-other-tip-in-dicom/</guid><description>&lt;h3 class="wp-block-heading" id="h-overview"&gt;Overview&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the &lt;a href="https://static.digihunch.com/2020/11/medical-imaging-web-server-deployment-pipeline/" class="rank-math-link"&gt;previous post &lt;/a&gt;I briefly touched on DICOM as the crucial standard in medical imaging for both data exchanging and data storage. It is important to understand that DICOM is such a massive standard that, beyond data exchanging and storage, has expanded into many different areas around imaging, that no device (or information system) can ever implement every single aspect of the standard. A device or information system complies to (and implements) a subset of the DICOM standard. The manufacturer must provide a document (DICOM conformance statement) to spec out which parts of the standard are implemented. Care providers (e.g. hospitals) are supposed to review these specs as part of the procurement process to ensure interoperability with existing information system.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-medical-imaging-informatics"&gt;Medical Imaging Informatics&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When it comes to diagnosis, historical examinations provide baseline reference for radiologist. They are sometimes even more revealing than current imaging data acquired from a patient. Compared to the current exams, historical ones are referred to as priors. Priors are useful only if they are relevant to the current exams in terms of body part, modality, and exam procedure (the particular problem being studied). The effort to find out and pre-load relevant priors so they are ready to display along with current exams, is called &amp;#8220;prefetch&amp;#8221;. With huge demand in exchanging imaging data, &amp;#8220;prefetch&amp;#8221; has developed into its own sub-market in imaging informatics industry. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Trust me, this is a difficult undertaking (and why I was in the industry). First, patient identities in each healthcare organization are usually different for lack of universal medical record number; patient&amp;#8217;s name can change (marriage, divorce, or just for fun); or it can be unavailable, if patient is simply not in a condition to provide identify (e.g. trauma). Second, even if you get &amp;#8220;who&amp;#8217;s who&amp;#8221; right, you&amp;#8217;d have to dig into all his history for useful information from several different systems. The definition of relevant prior can be different depending on the specific medical specialty. Then, the old data are typically stored in a slow part of the storage from their source system, yet the patient might be bleeding and dying on the table, waiting for prior retrieval like pulling teeth. Last, but not least, the priors being retrieve might be from a modality of previous generation from 1990s; good luck with current display application. Sorry that sounds a lot but in real life, there are even more challenges. In General, there are four categories of applications that need to support DICOM:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Acquisition devices: Modalities need to store newly acquired studies persistently;&lt;/li&gt;&#10;&lt;li&gt;Routing applications: usually by the name of some routers, gateways or bridges that receive imaging studies, decorate the metadata (because a lot of legacy devices can&amp;#8217;t do it), and send to one or multiple defined destination;&lt;/li&gt;&#10;&lt;li&gt;Archives (e.g. PACS, VNA): They usually use dedicated database (metadata) and storage systems (pixel data). They are the repository of imaging data and must provide full support of transfer capability;&lt;/li&gt;&#10;&lt;li&gt;Peripheral applications that uses DICOM data, such as 3D post-processing or DICOM testing (grassroot dicom, dcmtk) &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;No matter what the devices are, they must follow certain protocols in order to communicate with each other. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-data-structure-and-encoding"&gt;Data structure and encoding&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;DICOM PS3.5 defines data types in VR (value representation). Each VR has its own purpose, allowed characters, and length limit. For example:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;VR&lt;/th&gt;&lt;th&gt;Definition&lt;/th&gt;&lt;th&gt;Allowed Characters&lt;/th&gt;&lt;th&gt;Length Limit&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;SH&lt;br&gt;Short String&lt;/td&gt;&lt;td&gt;A string of characters&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;16 maximum&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;LO&lt;br&gt;Long String&lt;/td&gt;&lt;td&gt;A string of characters&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;64 maximum&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AE&lt;br&gt;Application Entity&lt;/td&gt;&lt;td&gt;A string of characters that identifies a DICOM application running on a compliant device&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;16 maximum&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CS&lt;br&gt;Code String&lt;/td&gt;&lt;td&gt;A string to represent code&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;16 maximum&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PN&lt;br&gt;Person Name&lt;/td&gt;&lt;td&gt;Person&amp;#8217;s name, with caret (^) as delimiter&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;64 maximum&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;UI&lt;br&gt;Unique Identifier&lt;/td&gt;&lt;td&gt;An ID that uniquely identify an item, such as 1.2.840.100008.1.1&lt;/td&gt;&lt;td&gt;0-9 and period (.)&lt;/td&gt;&lt;td&gt;64 maximum&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;DA&lt;br&gt;Date&lt;/td&gt;&lt;td&gt;A string to represent date YYYYMMDD&lt;/td&gt;&lt;td&gt;0-9&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;US&lt;br&gt;Unsigned Short&lt;/td&gt;&lt;td&gt;Unsigned binary integer, 16 bits long&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SQ&lt;br&gt;Sequence of other items&lt;/td&gt;&lt;td&gt;Sequence of other items&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;UN&lt;br&gt;Unknown&lt;/td&gt;&lt;td&gt;A string of bytes where the encoding of contents is unknown&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption class="wp-element-caption"&gt;Value Representatives&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;DICOM metadata is a dataset comprised of a set of data elements. Each element includes tag, (optional) VR, length of value, and the actual value, as shown below: &lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://3.bp.blogspot.com/-hAI3mF_ZL-I/TtQOMjy6LmI/AAAAAAAAKvE/SDqvwxaXnog/s1600/DICOM+Element.png" alt=""/&gt;&lt;figcaption class="wp-element-caption"&gt;DICOM data elements&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VR is optional because it can be implicitly determined based on DICOM data dictionary defined in PS3.6. The most common tags are:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Level&lt;/th&gt;&lt;th&gt;Tag&lt;/th&gt;&lt;th&gt;Meaning&lt;/th&gt;&lt;th&gt;VR&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Patient&lt;/td&gt;&lt;td&gt;0010,0010&lt;/td&gt;&lt;td&gt;Patient&amp;#8217;s Name&lt;/td&gt;&lt;td&gt;PN&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Patient&lt;/td&gt;&lt;td&gt;0010,0020&lt;/td&gt;&lt;td&gt;Patient ID&lt;/td&gt;&lt;td&gt;LO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Patient&lt;/td&gt;&lt;td&gt;0010,0021&lt;/td&gt;&lt;td&gt;Issuer of Patient ID&lt;/td&gt;&lt;td&gt;LO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Patient&lt;/td&gt;&lt;td&gt;0010,0024&lt;/td&gt;&lt;td&gt;Issuer of Patient ID Qualifier Sequence&lt;/td&gt;&lt;td&gt;SQ&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Patient&lt;/td&gt;&lt;td&gt;0010,0030&lt;/td&gt;&lt;td&gt;Patient&amp;#8217;s Birth Date&lt;/td&gt;&lt;td&gt;DA&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Study&lt;/td&gt;&lt;td&gt;0008,0020&lt;/td&gt;&lt;td&gt;Study Date&lt;/td&gt;&lt;td&gt;DA&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Study&lt;/td&gt;&lt;td&gt;0008,0050&lt;/td&gt;&lt;td&gt;Accession Number&lt;/td&gt;&lt;td&gt;SH&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Study&lt;/td&gt;&lt;td&gt;0008,0061&lt;/td&gt;&lt;td&gt;Modalities In Study&lt;/td&gt;&lt;td&gt;CS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Study&lt;/td&gt;&lt;td&gt;0008,1030&lt;/td&gt;&lt;td&gt;Study Description&lt;/td&gt;&lt;td&gt;LO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Study&lt;/td&gt;&lt;td&gt;0020,000D&lt;/td&gt;&lt;td&gt;Study Instance UID&lt;/td&gt;&lt;td&gt;UI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Study&lt;/td&gt;&lt;td&gt;0020,0010&lt;/td&gt;&lt;td&gt;Study ID&lt;/td&gt;&lt;td&gt;SH&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Series&lt;/td&gt;&lt;td&gt;0008,103E&lt;/td&gt;&lt;td&gt;Series Description&lt;/td&gt;&lt;td&gt;LO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Series&lt;/td&gt;&lt;td&gt;0008,0015&lt;/td&gt;&lt;td&gt;Body Part Examined&lt;/td&gt;&lt;td&gt;CS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Series&lt;/td&gt;&lt;td&gt;0008,0060&lt;/td&gt;&lt;td&gt;Modality&lt;/td&gt;&lt;td&gt;CS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Series&lt;/td&gt;&lt;td&gt;0020,0011&lt;/td&gt;&lt;td&gt;Series Number&lt;/td&gt;&lt;td&gt;IS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Series&lt;/td&gt;&lt;td&gt;0020,000E&lt;/td&gt;&lt;td&gt;Series Instance UID&lt;/td&gt;&lt;td&gt;UI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Series&lt;/td&gt;&lt;td&gt;0020,0060&lt;/td&gt;&lt;td&gt;Laterality&lt;/td&gt;&lt;td&gt;CS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SOP inst&lt;/td&gt;&lt;td&gt;0008,0016&lt;/td&gt;&lt;td&gt;SOP Class UID&lt;/td&gt;&lt;td&gt;UI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SOP inst&lt;/td&gt;&lt;td&gt;0008,0018&lt;/td&gt;&lt;td&gt;SOP Instance UID&lt;/td&gt;&lt;td&gt;UI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SOP inst&lt;/td&gt;&lt;td&gt;0012,0010&lt;/td&gt;&lt;td&gt;Transfer Syntax UID&lt;/td&gt;&lt;td&gt;UI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SOP inst&lt;/td&gt;&lt;td&gt;0020,0013&lt;/td&gt;&lt;td&gt;Instance Number&lt;/td&gt;&lt;td&gt;IS&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption class="wp-element-caption"&gt;Common DICOM tags&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When VR is not explicitly spelled out, the data encoding is known as implicit VR. The opposite is explicit VR, where each data element spells out the VR type. When storing numeric value, such as tags or numeric values for tags, the predominant format stores lower byte before higher bytes, known as &lt;a href="https://en.wikipedia.org/wiki/Endianness" class="rank-math-link"&gt;little endian&lt;/a&gt;. Rarely seen in DICOM is big endian, the opposite order of storing numeric values.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The tag include a group number (e.g. 0020) and element number (e.g. 0013). If group number is odd, it is a private tag not defined in PS3.6&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The actual length of tag value shall always be even number. Odd-sized value should add an additional character (e.g. trailing space), known as even-length padding to meet this requirement.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Above is the basic rules for DICOM data structure and encoding. For more information about encoding, including transfer syntax, refer to &lt;a href="https://static.digihunch.com/2018/06/dicom-data-encoding/" class="rank-math-link"&gt;this previous post&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-transactions-dimses-and-sop-classes"&gt;Transactions (DIMSEs and SOP classes)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;DICOM has its own &lt;a href="http://dicom.nema.org/medical/dicom/current/output/chtml/part04/chapter_6.html" class="rank-math-link"&gt;information model &lt;/a&gt;of real world. It requires some clinical knowledge to come to full understanding. For technical people, we just need to understand the patient-study-series-image hierarchy.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;One patient may have multiple studies&lt;/li&gt;&#10;&lt;li&gt;Each study may include one or more image series&lt;/li&gt;&#10;&lt;li&gt;Each series has one or more images&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Images are also referred to as SOP instance, a general terms that include not only images, but also reports, and other types of objects.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A business transaction in DICOM is termed DIMSE, for example:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Group&lt;/th&gt;&lt;th&gt;Type&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;C-STORE&lt;/td&gt;&lt;td&gt;DIMSE-C&lt;/td&gt;&lt;td&gt;Operation&lt;/td&gt;&lt;td&gt;A stores an image to B&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;C-MOVE&lt;/td&gt;&lt;td&gt;DIMSE-C&lt;/td&gt;&lt;td&gt;Operation&lt;/td&gt;&lt;td&gt;A tells B to store an image to C&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;C-FIND&lt;/td&gt;&lt;td&gt;DIMSE-C&lt;/td&gt;&lt;td&gt;Operation&lt;/td&gt;&lt;td&gt;Query for patient, study, series, images&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;C-ECHO&lt;/td&gt;&lt;td&gt;DIMSE-C&lt;/td&gt;&lt;td&gt;Operation&lt;/td&gt;&lt;td&gt;DICOM level &amp;#8220;ping&amp;#8221;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;N-EVENT-REPORT&lt;/td&gt;&lt;td&gt;DIMSE-N&lt;/td&gt;&lt;td&gt;Notification&lt;/td&gt;&lt;td&gt;Report an event&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;N-ACTION&lt;/td&gt;&lt;td&gt;DIMSE-N&lt;/td&gt;&lt;td&gt;Operation&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption class="wp-element-caption"&gt;DIMSE&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Some DIMSEs involves multiple SOP classes based on the type of image being processed. For example, SOP Class UID (10.2.840.10008.5.1.4.1.1.1) represents storage for CR image. A c-store transaction includes:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Requestor (C-STORE SCU) sends a request (C-STORE-RQ), to store specified SOP class in certain transfer syntax&lt;/li&gt;&#10;&lt;li&gt;The request is followed by the actual data (PDU)&lt;/li&gt;&#10;&lt;li&gt;Once completed, the Response (C-STORE SCP) respond with C-STORE-RP&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 class="wp-block-heading" id="h-handshake-association"&gt;Handshake (Association)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the previous C-STORE example, proper syntax that are mutually supported must be used in order for the requestor and receiver to process the transaction. Both parties learn each other&amp;#8217;s supported transfer syntax through an upfront handshake process known as DICOM association.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In DICOM association, the initiating party presents a list of supported pairs of SOP class and transfer syntax. Each pair is called a presentation context, and the responding party must respond to each presentation context in the proposed list, with either yes or no.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This negotiation is similar to the cipher negotiation in TLS handshake.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For more detailed information, please refer to Pianykh&amp;#8217;s book &amp;#8220;&lt;a href="https://www.springer.com/gp/book/9783642108495" class="rank-math-link"&gt;DICOM, a practical introduction and survival guide&lt;/a&gt;&amp;#8220;.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2020/11/medical-imaging-web-server-deployment-pipeline/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Automatic deployment of Orthanc on AWS&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2020/11/ipvs-iptables-and-kube-proxy/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;IPVS, iptables and kube-proxy&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>DataStax Python Driver</title><link>https://static.digihunch.com/2020/06/iterate-through-cassandra-table-with-datastax-python-driver/</link><pubDate>Sat, 27 Jun 2020 14:20:34 -0400</pubDate><guid>https://static.digihunch.com/2020/06/iterate-through-cassandra-table-with-datastax-python-driver/</guid><description>&lt;p class="wp-block-paragraph"&gt;For someone with relational database background, analyzing data in Cassandra isn&amp;#8217;t intuitive. There are two reasons. First, Cassandra data table is hardly updated or deleted in avoidance of tombstones. Insertion is the only action on the table resulting in multiple versions of each record all stored in the same table, thus a much longer table than its relational counterpart. Second, Cassandra schema is designed around how end-user will query the database, rather than a modelling of entity-relations. There are less fields, but some field may contain large data chunk, such as an entire XML document being stored in a column.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Data engineers with Cassandra may need to run full table scan, and extract values from wide columns of XML document by drilling down the XML tree structure, in order to produce a data frame (two-dimensional mutable, possibly heterogeneous tabular data structure with labeled rows and columns). &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I&amp;#8217;ve came across this task in the past and the duration of a full table scan on Cassandra table is in the order of hours, which is beyond what the built-in cqlsh tool can handle. I had to use Python to iterate through 200 million rows. Datastax Provides Cassandra client driver as a Python3 package, known as &lt;a href="https://docs.datastax.com/en/developer/python-driver/index.html"&gt;DataStax Python Driver&lt;/a&gt;. It allows us to build a simple Python3 script to complete a full table scan. The driver can be installed with pip3:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip3 install cassandra-driver&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;With the driver installed, we can start to pull data from Cassandra table into Python client class. here is a basic example of how to print the rows into a file:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! /usr/bin/python3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; cassandra.query &lt;span style="color:#f92672"&gt;import&lt;/span&gt; SimpleStatement&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; cassandra.cluster &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Cluster&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; cassandra &lt;span style="color:#f92672"&gt;import&lt;/span&gt; ConsistencyLevel&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; datetime&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; __name__ &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cluster &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Cluster([&lt;span style="color:#e6db74"&gt;&amp;#39;cass_host&amp;#39;&lt;/span&gt;],port&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;9042&lt;/span&gt;,protocol_version&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print (datetime&lt;span style="color:#f92672"&gt;.&lt;/span&gt;datetime&lt;span style="color:#f92672"&gt;.&lt;/span&gt;now()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strftime(&lt;span style="color:#e6db74"&gt;&amp;#34;%Y-%m-&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%d&lt;/span&gt;&lt;span style="color:#e6db74"&gt; %H:%M:%S&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34; start&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; session &lt;span style="color:#f92672"&gt;=&lt;/span&gt; cluster&lt;span style="color:#f92672"&gt;.&lt;/span&gt;connect(&lt;span style="color:#e6db74"&gt;&amp;#39;myownkeyspace&amp;#39;&lt;/span&gt;, wait_for_all_pools&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; query &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;SELECT * FROM mytable&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; statement &lt;span style="color:#f92672"&gt;=&lt;/span&gt; SimpleStatement(query, fetch_size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;, consistency_level&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ConsistencyLevel&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ONE)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; csv_file &lt;span style="color:#f92672"&gt;=&lt;/span&gt; open(&lt;span style="color:#e6db74"&gt;&amp;#39;result.csv&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;,&lt;span style="color:#ae81ff"&gt;8192&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; csv_file&lt;span style="color:#f92672"&gt;.&lt;/span&gt;write(&lt;span style="color:#e6db74"&gt;&amp;#34;header&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; tbrow &lt;span style="color:#f92672"&gt;in&lt;/span&gt; session&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(statement,timeout&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2.0&lt;/span&gt;):&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; csv_file&lt;span style="color:#f92672"&gt;.&lt;/span&gt;write(tbrow&lt;span style="color:#f92672"&gt;.&lt;/span&gt;user_id&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Exception&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; ex:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(ex)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;KeyboardInterrupt&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;Task Interrupted by SIGINT.&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;finally&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cluster&lt;span style="color:#f92672"&gt;.&lt;/span&gt;shutdown()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; csv_file&lt;span style="color:#f92672"&gt;.&lt;/span&gt;close()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print (datetime&lt;span style="color:#f92672"&gt;.&lt;/span&gt;datetime&lt;span style="color:#f92672"&gt;.&lt;/span&gt;now()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strftime(&lt;span style="color:#e6db74"&gt;&amp;#34;%Y-%m-&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%d&lt;/span&gt;&lt;span style="color:#e6db74"&gt; %H:%M:%S&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34; finish&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Note that the fetch_size can be set to larger number, but it may increase the chance of server read timeout (code=1200) in the middle of execution.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The processing logic can be implemented in the loop while each record in the table is being pulled out. The logic is repeated for every row so it will have a significant impact on the overall execution time.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Some the data needs to be ported into pandas data frame for further engineering, instead of being printed out to file. The following snippet will do the trick:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! /usr/bin/python3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; cassandra.query &lt;span style="color:#f92672"&gt;import&lt;/span&gt; SimpleStatement&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; cassandra.cluster &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Cluster&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; cassandra &lt;span style="color:#f92672"&gt;import&lt;/span&gt; ConsistencyLevel&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; datetime&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pandas &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; pd&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pandas_factory&lt;/span&gt;(colnames,rows):&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame(rows, columns&lt;span style="color:#f92672"&gt;=&lt;/span&gt;colnames))&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; res&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; __name__ &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cluster &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Cluster([&lt;span style="color:#e6db74"&gt;&amp;#39;cass_host&amp;#39;&lt;/span&gt;],port&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;9042&lt;/span&gt;,protocol_version&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print (datetime&lt;span style="color:#f92672"&gt;.&lt;/span&gt;datetime&lt;span style="color:#f92672"&gt;.&lt;/span&gt;now()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strftime(&lt;span style="color:#e6db74"&gt;&amp;#34;%Y-%m-&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%d&lt;/span&gt;&lt;span style="color:#e6db74"&gt; %H:%M:%S&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34; start&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; session &lt;span style="color:#f92672"&gt;=&lt;/span&gt; cluster&lt;span style="color:#f92672"&gt;.&lt;/span&gt;connect(&lt;span style="color:#e6db74"&gt;&amp;#39;myownkeyspace&amp;#39;&lt;/span&gt;, wait_for_all_pools&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; session&lt;span style="color:#f92672"&gt;.&lt;/span&gt;row_factory &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pandas_factory&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; query &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;SELECT * FROM mytable&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; statement &lt;span style="color:#f92672"&gt;=&lt;/span&gt; SimpleStatement(query, consistency_level&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ConsistencyLevel&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ONE,fetch_size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; df&lt;span style="color:#f92672"&gt;=&lt;/span&gt;pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; tbrow &lt;span style="color:#f92672"&gt;in&lt;/span&gt; session&lt;span style="color:#f92672"&gt;.&lt;/span&gt;execute(statement):&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; df&lt;span style="color:#f92672"&gt;=&lt;/span&gt;df&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(tbrow&lt;span style="color:#f92672"&gt;.&lt;/span&gt;user_id,ignore_index&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Exception&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; ex:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(ex)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;KeyboardInterrupt&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;Task Interrupted by SIGINT.&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;finally&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cluster&lt;span style="color:#f92672"&gt;.&lt;/span&gt;shutdown()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print (datetime&lt;span style="color:#f92672"&gt;.&lt;/span&gt;datetime&lt;span style="color:#f92672"&gt;.&lt;/span&gt;now()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strftime(&lt;span style="color:#e6db74"&gt;&amp;#34;%Y-%m-&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%d&lt;/span&gt;&lt;span style="color:#e6db74"&gt; %H:%M:%S&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34; finish&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In my test environment with 180 million rows in the table, the execution of the first script takes 37 minutes (of course there&amp;#8217;s a lot of factors at play). I experimented several approaches to improve the speed, such as tuning the &lt;a href="https://medium.com/@bramblexu/understand-the-buffer-policy-in-python-78e91e7759ca"&gt;buffering options&lt;/a&gt; for file write. However, It turns out that the speed bottleneck of the script is not even file IO, but rather pulling data out of Cassandra.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The output can be stored as CSV file, which can be lately loaded to relational database for analysis. PostgreSQL would be a good open-source choice because it is both transactional and analytical.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2020/06/performance-analysis-tools/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Performance Analysis&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2020/07/dockersnetwork/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Docker network in different modes&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>