Jekyll2022-08-23T22:13:37+00:00https://blancas.io/feed.xmlEduardo BlancasPersonal websiteEduardo Blancasedu@blancas.ioDon’t make users read your docs2022-07-23T00:00:00+00:002022-07-23T00:00:00+00:00https://blancas.io/blog/users-and-docs<p>As an <a href="https://github.com/ploomber/ploomber">open-source maintainer</a>, I always put effort into documenting all known edge cases so that users know how to fix problems. So, whenever users report incompatibilities, we highlight them in our documentation. Still, I realized this approach wasn’t working when users came to our Slack asking for help with problems we had already documented.</p>
<p>As project maintainers, we tend to be overly optimistic about how good the documentation is. But the target metric should not be how detailed our documentation is but how fast users can get things done. And when things go wrong, reading the documentation is not always the quickest route, so <em>don’t make your users read your docs, help them right on the spot.</em></p>
<h2 id="motivating-example">Motivating example</h2>
<p>A few weeks ago, a user <a href="https://github.com/ploomber/ploomber/issues/882">reported an issue</a>. The details are not important, but it required us to add a new argument to a class. We added the argument to the constructor, documented it, and posted the solution in the GitHub issue; however, when thinking about what would happen if a new user had the same issue, I realized we solved the problem for one user but not the rest. Most likely, other users would have a hard time trying to fix the issue, and most likely, they’d give up if they didn’t find the answer quickly.</p>
<h2 id="useful-error-messages">Useful error messages</h2>
<p>A helpful error message tells you three things:</p>
<ol>
<li>What failed</li>
<li>Why it failed</li>
<li>How to fix it</li>
</ol>
<p>For example:</p>
<blockquote>
<p>RuntimeError: Cannot re-initialize CUDA in forked subprocess. To use CUDA with multiprocessing, you must use the ‘spawn’ start method</p>
</blockquote>
<p>This error message contains the three elements:</p>
<ol>
<li>Cannot re-initialize CUDA [What failed]</li>
<li>…in forked process [Why it failed]</li>
<li>Use the ‘spawn’ start method [How to fix it]</li>
</ol>
<p>The problem is that our framework builds an abstraction, so users don’t have to use the <code class="language-plaintext highlighter-rouge">multiprocessing</code> module directly; hence, the user couldn’t fix the issue unless they modified the source code.</p>
<p>In our specific use case, here’s a better error message:</p>
<blockquote>
<p>RuntimeError: Cannot re-initialize CUDA in forked subprocess. To use CUDA with multiprocessing, Pass ‘spawn’ to the ‘start_method’ argument of the Parallel executor constructor</p>
</blockquote>
<p>Let’s see how to achieve this.</p>
<h2 id="helpful-error-messages">Helpful error messages</h2>
<p><em>Note: the following sections contain Python code snippets, but the idea applies to any language.</em></p>
<p>We want to anticipate the error and tell the user how to get things running:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">some_package.exceptions</span> <span class="kn">import</span> <span class="n">SomeException</span>
<span class="k">def</span> <span class="nf">thing_that_breaks</span><span class="p">(</span><span class="n">argument</span><span class="p">):</span>
<span class="p">...</span>
<span class="k">def</span> <span class="nf">thing_that_the_user_calls</span><span class="p">(</span><span class="n">argument</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">thing_that_breaks</span><span class="p">(</span><span class="n">argument</span><span class="o">=</span><span class="n">argument</span><span class="p">)</span>
<span class="k">except</span> <span class="n">SomeException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="c1"># add more context and raise whatever exception type makes sense
</span> <span class="k">raise</span> <span class="nb">RuntimeError</span><span class="p">(</span><span class="s">'How to fix it'</span><span class="p">)</span> <span class="k">from</span> <span class="n">e</span>
<span class="k">except</span><span class="p">:</span>
<span class="c1"># raise the original exception, unmodified
</span> <span class="k">raise</span>
<span class="p">...</span>
</code></pre></div></div>
<p><em>Note:</em> the <code class="language-plaintext highlighter-rouge">raise exception from another_exception</code> expression is called a <a href="https://peps.python.org/pep-3134/">chained exception</a> in Python.</p>
<p>The previous snippet will provide the user-specific instructions when encountering the problem using our software.</p>
<p>However, we’re assuming that:</p>
<ol>
<li>We can import <code class="language-plaintext highlighter-rouge">some_package.exceptions</code> in our project’s codebase (which implies adding it as a dependency)</li>
<li>We are sure that when <code class="language-plaintext highlighter-rouge">SomeException</code> is raised, the solution is what we are displaying to the user</li>
</ol>
<p>Sometimes exceptions are too general, so we need to dig deeper. In such cases, we can use the error message as a proxy:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">thing_that_breaks</span><span class="p">(</span><span class="n">argument</span><span class="p">):</span>
<span class="p">...</span>
<span class="k">def</span> <span class="nf">thing_that_the_user_calls</span><span class="p">(</span><span class="n">argument</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">thing_that_breaks</span><span class="p">(</span><span class="n">argument</span><span class="o">=</span><span class="n">argument</span><span class="p">)</span>
<span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">if</span> <span class="s">'some hint'</span> <span class="ow">in</span> <span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">):</span>
<span class="k">raise</span> <span class="nb">Exception</span><span class="p">(</span><span class="s">'Instructions on how to fix it'</span><span class="p">)</span> <span class="k">from</span> <span class="n">e</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span>
<span class="p">...</span>
</code></pre></div></div>
<p>There are obvious drawbacks to this approach: the error message might change, although the same is true for the exception type, so in any case, ensure you have unit tests in place.</p>
<p>I’ve encountered cases where checking the error message isn’t enough, and we might display inaccurate instructions. In such situations, I write an error to reflect that:</p>
<blockquote>
<p>If having issues with X, try [possible solution]</p>
</blockquote>
<h2 id="the-end">The end</h2>
<p>If you enjoyed this, let’s connect on <a href="https://twitter.com/edublancas">Twitter</a>, where I often post my adventures as open-source maintainer, and if you do Data Science, check out our <a href="https://github.com/ploomber/ploomber">project.</a></p>Eduardo Blancasedu@blancas.ioAs an open-source maintainer, I always put effort into documenting all known edge cases so that users know how to fix problems. So, whenever users report incompatibilities, we highlight them in our documentation. Still, I realized this approach wasn’t working when users came to our Slack asking for help with problems we had already documented.Introducing ploomber2020-02-21T00:00:00+00:002020-02-21T00:00:00+00:00https://blancas.io/blog/ploomber<p>Update: Ploomber’s related content will be posted in a <a href="https://ploomber.io/">dedicated website</a>.</p>
<p>Today I am announcing the release of <a href="https://github.com/ploomber/ploomber">ploomber</a>, a library to accelerate Data Science and Machine Learning pipeline experimentation. This post describes the motivation, core features, short and medium-term objectives.</p>
<h2 id="motivation">Motivation</h2>
<p>When I started working on Data Science projects back in 2015, I realized that there were no standard practices for developing pipelines; which caused teams to develop fragile, hardly reproducible software. During one of my first projects, our pipeline consisted of a bunch of shell, SQL and Python scripts loosely glued together; each member would edit a “master” shell script so we could “reproduce” our end result, but since our pipeline would take several hours to run, no one would test that such script actually worked, hence, there was no guarantee that given a clean environment, the pipeline would execute without errors, let alone give the same final output.</p>
<p>Back then, a few colleagues started using <a href="https://github.com/Factual/drake">drake</a> (currently unmaintained) whose purpose was to manage complex data workflows. The concept caught my attention but it had two drawbacks 1) it is written in Clojure - which prevented me from digging into the codebase and 2) it resolved dependencies using file timestamps, thus assuming that every task produced files in the local filesystem, which does not hold for many pipelines interacting with remote systems.</p>
<p>Later next year, I found out a few other projects: <a href="https://github.com/spotify/luigi">Luigi</a>, <a href="https://github.com/pinterest/pinball">Pinball</a> and <a href="https://github.com/apache/airflow">Airflow</a> . I think they are great, fully featured Data Engineering tools but they are not not what I was looking for. I tried Luigi and Airflow for two small projects but became frustrated with their setup and a steep learning curve; however, the primary reason I do not use them anymore is the lack of consideration to the iterative nature of Data Science/Machine Learning pipelines, which involves trying out new experiments (very often interactively) to improve a final result (e.g. a predictive model), their motivations were more aligned with the Data Engineering space (which is reflected in their documentation examples). I wanted something that kept track of my source code changes and update results accordingly, which I believe is what <a href="https://github.com/ropensci/drake">drake from rOpenSci </a> (not related to the first one I mentioned) does.</p>
<p>Then I started graduate school and participated in another data project: <a href="https://github.com/paninski-lab/yass/">YASS</a>, a library for computational Neuroscience. One of my contributions was to make sure that the pipeline could be used by other teams, so I refactored the code in modules and created a “pipeline function” that would import all the tasks and execute them in the right order. I setup a CI service and coded a few <a href="https://en.wikipedia.org/wiki/Smoke_testing_(software)">smoke tests</a> that executed the pipeline with some sample inputs. The pipeline had a lot of parameters that had to be customized by the user; requiring them to write their own “pipeline function” seemed to much, so we ended up using a common approach: a configuration file. This worked great for a while, until we started to experiment with the pipeline to enhance it. Any modification faced a challenge: adding new parameters meant modifying logic to validate the configuration file, which severely limited our ability to try out new experiments. While the configuration file still made sense for some end users, it did not make it for us, the developers. Since we wanted to add and remove building blocks, each one could potentially have their own “pipeline function”, but given the absence of a standard API for building blocks (where to save the data, what kind of input to expect), small changes would break the pipeline.</p>
<p>Based in my experience, I had a clear idea of which tool I needed. Since no existing tool fulfilled all my requirements, I started building a small library to facilitate experimentation for my current projects. As my projects grew in complexity, I found myself spending more and more time to fix bugs and develop new features until it reached a stable status. For a the second half of 2019, the codebase remained private and very few changes were introduced. Keeping that private benefits no one other than me, so here it is.</p>
<h2 id="features">Features</h2>
<h3 id="expressive-syntax">Expressive syntax</h3>
<p>My main learning when working on YASS was that each team member should be able to experiment with the pipeline structure by re-organizing building blocks. I think Airflow’s syntax achieves that, each task is an object and dependencies are declared using an explicit operation. ploomber borrows Airflow’s expressive syntax with a few twists so pipeline declarations read like blueprints.</p>
<p>An important aspect I have seen overlooked in existing libraries is that even though tasks produce persistent changes (e.g. a new file, a table in a database), they are not part of the pipeline declaration but hidden in the task implementation. In my experience, this is a huge source of errors: paths to files (or table names) are hardcoded everywhere in the codebase, which leads to files getting deleted or overwritten accidentally. ploomber requires each task to declare its output, hence the pipeline declaration provides a complete picture: it not only includes which tasks to perform and in which order, but where the output will be stored and in which form.</p>
<p>This allows the same pipeline declaration to be easily customized by team members (e.g. each member stores outputs in <code class="language-plaintext highlighter-rouge">/data/YOUR_USERNAME/output</code>) effectively isolating pipeline runs among each other.</p>
<h3 id="standalone-execution">Standalone execution</h3>
<p>One of the most common features I have seen in existing tools is support for containerization and orchestration, while these features are required for complex applications; even moderately complex pipelines (especially during the development phase) can achieve great progress by just having a virtual environment in a single machine. This is even true in the “big data” regime, where many applications can be solved by deferring computations to another system (such as an analytical database). In such cases, the pipeline just sends messages to other systems, with little to no computation happening in the client. Requiring every user to worry about containerization or orchestrations leaves out a lot of potential users that could benefit from a workflow management tool (e.g. scientists developing models in their laptops). In ploomber, once a pipeline is instantiated, it is ready to run.</p>
<p>There are of course cases when orchestration is needed, but I believe this is a separate problem to be solved by a different tool, rather than bundled it together in one solution.</p>
<h3 id="incremental-builds">Incremental builds</h3>
<p>Pipelines usually take hours or even days to run, during the development phase, it is wasteful to re-execute the pipeline on each code change. Building complex software systems is a similar process, which is why tools such as <a href="https://www.gnu.org/software/make/">GNU Make</a> exist. Taking a similar approach, ploomber keeps track of code changes and only executes a task if the source code has changed since its last execution. Since for many data pipelines outputs might not even exist in the same machine (e.g. the pipeline creates a table in a remote database), ploomber is able to check status in external systems.</p>
<h3 id="testable-and-interactive">Testable and interactive</h3>
<p>ploomber produces standalone pipelines that are able to execute themselves (they are just Python objects) this makes testing much easier. For my current projects, I have the usual <code class="language-plaintext highlighter-rouge">tests/</code> folder where I import a function that instantiates a pipeline object, then I run the pipeline in a testing environment (to isolate it from the development environment) with a data sample, which makes them run end-to-end in a reasonable time.</p>
<p>ploomber also supports hooks to execute code upon task completion. Which allows me to write <a href="https://en.wikipedia.org/wiki/Acceptance_testing">acceptance tests</a> that explicitly state input assumptions (e.g. check a data frame’s input schema). Not stating data assumptions explicitly is a common source of errors when they do not hold anymore causing errors propagate to downtream tasks (e.g. a column that suddenly has NAs breaks a sum computation).</p>
<p>The combination of both types of tests serves different purposes: <code class="language-plaintext highlighter-rouge">tests/</code> allows me to check whether the pipeline still works after a few code changes and acceptance tests prevent unexpected conditions to propagate downstream in the pipeline.</p>
<p>Pipelines support more than just a <code class="language-plaintext highlighter-rouge">build</code> command. You can (among other things) interact with any individual task by doing <code class="language-plaintext highlighter-rouge">dag['task_name']</code>, which is useful for debugging and by doing <code class="language-plaintext highlighter-rouge">dag['task_name'].debug()</code> you can start a debugging session (using the <code class="language-plaintext highlighter-rouge">pdb</code> module).</p>
<h3 id="communicable">Communicable</h3>
<p>Data projects usually involve several stakeholders spanning all kind of backgrounds. Explaining the pipeline’s embedded logic, assumptions and resources needed is key to build trust on it. While the pipeline declaration provides a blueprint for developers, it is not well-suited non-technical partners (or even technical ones from other disciplines such as software engineers or business analysts). ploomber is able to generate HTML summaries with code, output location and a diagram to communicate pipelines to a wider audience.</p>
<h2 id="present-and-future">Present and future</h2>
<p>My short-term goal is to consolidate ploomber as a robust and accessible tool by increasing testing coverage and improving documentation. The API has matured enough and it is not expected to change (besides trivial changes).</p>
<p>The medium-term goal is to improve current features to ease pipeline debugging and communication. There are some important features I will leave out of this project (containerization, orchestration, scheduling and serving), but I am currently evaluating tools to integrate with.</p>
<p>ploomber is available on <a href="https://github.com/ploomber/ploomber">Github</a> and I am already using it in production projects. I hope it helps you in your next DS/ML pipeline!</p>Eduardo Blancasedu@blancas.ioUpdate: Ploomber’s related content will be posted in a dedicated website.5 signs your Data Science workflow is broken2019-07-16T00:00:00+00:002019-07-16T00:00:00+00:00https://blancas.io/blog/ds-broken-workflow<p>Developing reproducible data pipelines is hard, but before we even think about reproducibility your project has to meet some minimum standards. This post discusses some recurring bad practices when developing data pipelines and provides some advice to overcome them.</p>
<h2 id="1-lack-of-setup-instructions">1. Lack of setup instructions</h2>
<p>The first step for every software project is to get the environment up a running (e.g., install UNIX package A, then install Python 3.7, then install Python libraries X, Y and Z), however, more often than not, the environment is setup once and instructions are never recorded.</p>
<p>Data Science projects often depend on complex software setups (e.g., installing GPU or database drivers); lack of instructions will surely cause a lot of trouble for the team, especially when a new member joins or when the project is taken to a production environment.</p>
<p>This setup instructions have to be always up to date, they will break if a single new dependency is not registered and unnecessarily complex if any dependency is no longer needed.</p>
<p><strong>How to fix it?</strong> All projects should come with a shell script to setup the project, package managers do the heavy lifting for installing software so you might assume that one is already installed.</p>
<p>To prevent setup instructions become outdated, test them every time your code changes by using a <a href="Continuous Integration">Continuous Integration</a> service such as <a href="https://travis-ci.org/">Travis CI</a>. While CI services can detect when your dependencies no longer work, they cannot detect unnecessary libraries, those you have to remove manually from the setup script.</p>
<h2 id="2-environment-configuration-embedded-in-the-source-code">2. Environment configuration embedded in the source code</h2>
<p>If you keep seeing this error message when running your pipeline:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s2">"/Users/coworkersname/data/clean_v2.parquet"</span> file not found.
</code></pre></div></div>
<p>It is probably because someone in the team hardcoded a path to a file/directory that only exists in their machine. Even if you are working in a shared filesystem, it is a good idea to keep files separate to prevent accidentally overwriting their work. <strong>Explicit paths should never make it to the code.</strong></p>
<p><strong>How to fix it?</strong> Keep all things such as I/O paths and host addresses in a separate place and read from there. For example, you might have a file like this in your project’s root directory:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># locations.yaml</span>
<span class="na">data</span><span class="pi">:</span>
<span class="c1"># all raw data goes here</span>
<span class="na">raw</span><span class="pi">:</span> <span class="s">~/project/data/raw/</span>
<span class="c1"># all processed data goes here</span>
<span class="na">processed</span><span class="pi">:</span> <span class="s">~/project/data/processed</span>
<span class="c1"># host to the database</span>
<span class="na">db</span><span class="pi">:</span> <span class="s">db.organization.com:5421/database</span>
</code></pre></div></div>
<p>Everyone then should treat that file as a <em>contract</em>: you must read and write only to these directories. Each member can customize their configuration file and nothing should break. Your code will look like this:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
<span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">from</span> <span class="nn">my_project</span> <span class="kn">import</span> <span class="n">locations</span>
<span class="k">def</span> <span class="nf">clean_data</span><span class="p">():</span>
<span class="c1"># load content of locations.yaml
</span> <span class="n">path_raw</span> <span class="o">=</span> <span class="n">locations</span><span class="p">[</span><span class="s">'data'</span><span class="p">][</span><span class="s">'raw'</span><span class="p">]</span>
<span class="n">path_clean</span> <span class="o">=</span> <span class="n">locations</span><span class="p">[</span><span class="s">'data'</span><span class="p">][</span><span class="s">'processed'</span><span class="p">]</span>
<span class="c1"># read a file relative to the raw data folder...
</span> <span class="n">pd</span><span class="p">.</span><span class="n">read_parquet</span><span class="p">(</span><span class="n">Path</span><span class="p">(</span><span class="n">path_raw</span><span class="p">,</span> <span class="s">'dataset.parquet'</span><span class="p">))</span>
<span class="c1"># clean the data...
</span>
<span class="c1"># write to a file relative to the clean data folder...
</span> <span class="n">pd</span><span class="p">.</span><span class="n">to_parquet</span><span class="p">(</span><span class="n">Path</span><span class="p">(</span><span class="n">path_clean</span><span class="p">,</span> <span class="s">'dataset.parquet'</span><span class="p">))</span>
</code></pre></div></div>
<p>Make sure the file is easily discoverable inside your scripts, you might want to create a function that automatically finds a <code class="language-plaintext highlighter-rouge">locations.yaml</code> file in the current working directory or any parent folders up to certain levels and raises and <code class="language-plaintext highlighter-rouge">Exception</code> if it cannot find one.</p>
<h2 id="3-end-to-end-pipeline-execution-requires-manual-intervention">3. End-to-end pipeline execution requires manual intervention</h2>
<p>A pipeline is not such if it needs manual intervention to run. Given the raw data, you should be able to run the pipeline end-to-end with a single command. For starters, that means you should only use scripting tools such as Python or R, and no GUI tool such as Excel.</p>
<p>Automated execution is a prerequisite for automated testing. Bugs are inevitable, but automated testing can save you from finding those bugs in a production environment.</p>
<p><strong>How to fix it?</strong> If setup instructions are provided and there are not hardcoded paths, having an automated pipeline will be easier. As with setup instructions, the only reliable way to keep this working is to include a shell script in the CI service to make sure your pipeline still runs. If you are working with large datasets, you may want to pass a sample of the data for testing purposes.</p>
<h2 id="4-intermediate-results-are-shared-over-e-mailcloud-storage">4. Intermediate results are shared over e-mail/cloud storage</h2>
<p>A (unfortunate) common practice in many data analysis projects is to share intermediate results. Reasons vary but the pattern goes like this: member <code class="language-plaintext highlighter-rouge">A</code> updates some code in the pipeline that <code class="language-plaintext highlighter-rouge">B</code> needs as input, so <code class="language-plaintext highlighter-rouge">A</code> runs the updated code and shares the new results with <code class="language-plaintext highlighter-rouge">B</code>, who then uses this new file as input instead of the old version.</p>
<p>Sharing intermediate results is a terrible practice since it makes reproducibility harder. <strong>Intermediate results should never be shared</strong>, <code class="language-plaintext highlighter-rouge">A</code> should just push the new code and <code class="language-plaintext highlighter-rouge">B</code> should execute it to get the new input to use.</p>
<p><strong>How to fix it?</strong> Fixing this pattern is harder, all previous sections are prerequisites for this one, namely:</p>
<ol>
<li>There should a setup script to configure setup the environment</li>
<li>Configuration should be centralized in a single file, out of the source code</li>
<li>There should be a script to execute the pipeline end-to-end</li>
</ol>
<p>If all those requisites are met, there is no need to share intermediate files.</p>
<p>The only situation when sharing intermediate files might be necessary is when any of your tasks either a) takes <em>a lot</em> to run or b) it has to be run in a restricted environment (e.g., a shared cluster). In such case, special care should be given to make sure that the code that produced some results is appropriately stored in version control. <strong>Avoid this situation as much as possible.</strong></p>
<p>For most projects, this should not be the case. If you are working with large datasets, you probably already have some distributed infrastructure which makes your computationally heavy scripts run in a reasonable amount of time, if they do not, consider splitting them in smaller steps.</p>
<h2 id="5-a-change-in-a-single-step-requires-you-to-execute-the-pipeline-end-to-end">5. A change in a single step requires you to execute the pipeline end-to-end</h2>
<p>During development, it is always the case that steps are revisited (added features, fixed bugs). Every time you make a change, you have to make sure that all changes propagate to steps downstream. Since steps in a data pipeline often take minutes or even hours to run, an update should only trigger execution on their downstream dependencies to avoid wasteful computations.</p>
<p>If there is no way for your pipeline to know which steps are affected by any given update you only have two choices: either to run the entire pipeline again or manually check which steps have to be run. Both options are a waste of your time.</p>
<p><strong>How to fix it?</strong> There is not a single answer here. I have not found any library to easily fix this issue (I implemented my own solution but it is not publicly available yet). If all your processing is done locally, my recommendation is to use <a href="https://en.wikipedia.org/wiki/Make_(software)">Make</a>.</p>
<h2 id="final-comments">Final comments</h2>
<p>I hope this post helps you find areas for improvement in your data projects. Putting attention to this issues will pay off in the long run. A working workflow not only will increase your productivity to get your analysis right faster but will help you build more robust data products.</p>Eduardo Blancasedu@blancas.ioDeveloping reproducible data pipelines is hard, but before we even think about reproducibility your project has to meet some minimum standards. This post discusses some recurring bad practices when developing data pipelines and provides some advice to overcome them.The case against data versioning2019-06-27T00:00:00+00:002019-06-27T00:00:00+00:00https://blancas.io/blog/ds-versioning-data<p>A recent technique to advocate for reproducibility in data analysis is <em>data versioning</em>, which means that some (or all) intermediate files generated by the pipeline are saved and tagged so we can come back to them at any moment. But I think data versioning is actually <em>harmful</em> for reproducibility.</p>
<p>Reproducibility is defined as the <em>“ability of a researcher to duplicate the results of a prior study using the same materials as were used by the original investigator. Reproducibility is a minimum necessary condition for a finding to be believable and informative <a href="https://stm.sciencemag.org/content/8/341/341ps12.full">(Source)</a>.”</em> The key term here is <em>materials</em>. The only materials in a data pipeline are the raw data and the code, all other artifacts are byproducts which should not be considered.</p>
<p>We can test for reproducibility by answering the following question: given the <em>same raw data and code</em>, do we get the same results? Using intermediate results and claim reproducibility is cheating, since we are overlooking all previous computations that originated such interim results.</p>
<p><em>Reproducibility can only be achieved by construction</em>, it is not a feature you add to your pipeline. The bad news is that you cannot do <code class="language-plaintext highlighter-rouge">pip install reproducibility</code>; the only way to achieve it is through better software engineering practices. The good news is that verifying reproducibility is trivial.</p>
<p><strong>Verifying reproducibility</strong></p>
<p>Verifying that a data pipeline is reproducible is as simple as passing <em>raw data</em> and comparing the <em>claimed final output</em>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">pipeline</span>
<span class="n">result_final</span> <span class="o">=</span> <span class="n">pipeline</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="s">'/path/to/raw/data.csv'</span><span class="p">)</span>
<span class="n">result_expected</span> <span class="o">=</span> <span class="mi">42</span>
<span class="k">assert</span> <span class="n">result_final</span> <span class="o">==</span> <span class="n">result_expected</span>
</code></pre></div></div>
<p><strong>Saving intermediate results is useful (for other purposes)</strong></p>
<p>Data pipelines are built from steps that run one after the other, when the final output is unexpected, storing intermediate results makes our pipeline more transparent: we can go inspect those results and identify which step went wrong.</p>
<p>They are also useful for avoiding redundant computation. Pipelines usually take a lot of time to run, doing an end-to-end run every time we make a small change is wasteful. Selectively running the steps affected by the changes should lead us to the same result had we executed the pipeline end-to-end.</p>
<p><strong>Versioning your final output</strong></p>
<p>There is one piece of your pipeline that you can version: the final output. If you want to automate reproducibility verification, then you might want to store the final output to compare with the pipeline’s output whenever a change is introduced. If results do not match, your pipeline is no longer reproducible.</p>Eduardo Blancasedu@blancas.ioA recent technique to advocate for reproducibility in data analysis is data versioning, which means that some (or all) intermediate files generated by the pipeline are saved and tagged so we can come back to them at any moment. But I think data versioning is actually harmful for reproducibility.Aplicando a una maestría en EE.UU. (II): Seleccionando programas2019-04-07T00:00:00+00:002019-04-07T00:00:00+00:00https://blancas.io/blog/study-us-ii<p>En esta segunda parte escribiré sobre qué parámetros tomar en cuenta para elegir los programas a los que aplicarás. Considera unos 10-15 programas en tu búsqueda, para finalmente aplicar a unos 6. Lo más importante es la calidad del programa pero considera que entre más prestigio tenga, más competitiva será la admisión, el resto de los aspectos que mencionaré no están en ningún orden particular, dependerá de ti cuál consideres más importante.</p>
<h2 id="aspectos-a-considerar">Aspectos a considerar</h2>
<p><em>tl;dr; investiga la calidad del programa en diversas fuentes, asegúrate que tu programa tenga eventos de reclutamiento, considera que los costos son muy variables y que necesitarás demostrar solvencia económica para obtener la visa</em></p>
<h3 id="calidad-del-programa">Calidad del programa</h3>
<p>El parámetro más importante es la calidad del programa pero también es el más difícil de evaluar. Usar los rankings es algo engañoso porque hay escuelas que son conocidas solo en algunas áreas, por lo que no figuran en los rankings generales (ejemplo: Carnegie Mellon en Computer Science). Es mejor guiarse por los rankings por área, pero aún en ese caso, puede haber más de un programa en el mismo departamento. Mi recomendación es usar todos los recursos que tengas a tu alcance, y pon especial énfasis en los detalles de cada programa. La mejor forma de obtener información más detallada sobre programas en específico es hablar con algún graduado (LinkedIn es un buen recurso para esto).</p>
<p>Pon atención en cuestiones como: materias obligatorias, oferta de optativas, posibilidad de tomar materias en otros departamento o escuelas cercanas, tamaño del programa (un programa de 30 personas es una experiencia muy diferente a uno de 200), estadísticas de los graduados.</p>
<h3 id="salida-laboral">Salida laboral</h3>
<p>Si tienes interés en hacer una pasantía durante el verano o trabajar temporalmente después de tu programa (tanto la visa de estudiante F1 como la J1 te permiten hacerlo) es importante que investigues si los programas tienen ferias de reclutamiento; en mi experiencia, esta es una de las mejores formas de obtener una entrevista (la otra es obtener una recomendación de alguien que trabaje ahí). Es importante que investigues por reclutamientos exclusivos para el programa de tu interés, los eventos a nivel escuela no son tan efectivos, va tanta gente que no tienes suficiente tiempo de entablar una conversación con los reclutadores.</p>
<h3 id="costo-del-programa">Costo del programa</h3>
<p>Este punto es el más tedioso, sobre todo si apenas te encuentras buscando opciones, pero informarte con antelación te evitará muchas complicaciones después. El costo anual de colegiatura varía entre universidades y los costos de vida varían aún <em>mucho más</em> de ciudad a ciudad (vivir en NYC es mucho más caro que vivir en Austin). Al momento de solicitar la visa, tendrás que probar solvencia económica a través de una cuenta bancaria, documentos de becas obtenidas, préstamos, etc. Es muy probable que en esta etapa del proceso aún no sepas si se te ha otorgado alguna beca, por lo que si dependes de alguna beca para cubrir los gastos, seleccionar un programa más asequible disminuirá el riesgo de complicaciones si la beca no te es otorgada (en el siguiente artículo me enfocaré en hablar de las opciones de becas, créditos y otras opciones para financiar tu programa).</p>
<p>Pocos programas a nivel maestría ofrecen becas y suelen otorgarlas cuando envían tu carta de aceptación, así que si hay alguno de tu interés que ofrezca beca, tómalo en cuenta. La duración del programa es muy importante porque impacta directamente en el costo, la mayoría son de dos años pero existen también de 3 semestres e incluso de un año. A pesar de que un programa más largo te dará más tiempo para profundizar tu aprendizaje, el aumento en el costo será considerable.</p>
<h3 id="ubicación-de-la-universidad">Ubicación de la universidad</h3>
<p>Además del impacto económico que tiene la ubicación de la universidad, también impactará tu experiencia. En primer lugar debes considerar que algunas universidades están ubicadas en ciudades grandes (Nueva York, Chicago), otras en ciudades no tan grandes (Boston) y otras en donde <a href="https://en.wikipedia.org/wiki/College_town">lo único que hay es la universidad</a>. Ciertamente la calidad de tu programas es mucho más importante que la oferta de entretenimiento en la ciudad, pero al menos haz el ejercicio mental de imaginarte viviendo en tal o cual ciudad y asegúrate que te sentirás cómodo ahí.</p>
<p>Por otro lado, la ubicación también afectará tus prospectos laborales. A pesar de que la mayoría de las solicitudes se realizan por internet y nada te impide irte a trabajar a otra ciudad, la cercanía con la industria hará más sencillo este proceso. Para empezar, las empresas suelen reclutar en las universidades locales, además, si empiezas a buscar trabajo antes de graduarte, será mucho más fácil acudir a las entrevistas si las empresas están en la misma ciudad (la última ronda de entrevistas siempre es en la oficina de la empresa), en caso contrario, tu disponibilidad para tomar las entrevistas estará limitado por el tiempo que puedas ausentarte sin afectar tu rendimiento escolar.</p>
<h2 id="a-cuántos-programas-aplicar">¿A cuántos programas aplicar?</h2>
<p><em>tl;dr; aplica a 6 programas, elige los dos que más quieras, dos no tan competitivos y dos opciones “seguras”</em></p>
<p>Una vez que tengas una lista de unos 10-15 programas, es momento de decidir a cuales aplicarás. Toma en cuenta que la solicitud a cada programa tendrá un costo de 75-100 USD, pero aún más importante es que tendrás que enviar la carta de motivos y cartas de recomendación <em>diferentes</em> a cada programa. ¿Qué tan diferentes? Eso depende de ti. A pesar de que puedes enviar las mismas cartas, considero que es una muy mala estrategia pues demostrará poco interés de tu parte. Como mínimo, deberás incluir en tu carta (y quienes te recomienden en las suyas) el nombre de la universidad y el programa; idealmente, una porción de tu carta de motivos estará dedicada a hablar de los detalles de cada programa para escribir por qué deberían admitirte (pedirle una carta de recomendación diferente por programa es muy complicado, así que como mínimo pide que cambien el nombre).</p>
<p>Por otro lado, entre menos solicitudes envíes, más riesgo habrá de que no quedes en ningún programa (sí, eso pasa, sobre todo en programas competitivos donde los porcentajes de admisión suelen ser de un dígito). Te recomiendo que no apliques a menos de 6 programas; todos ellos deben ser programas en los que si recibes admisión estes bien seguro de que te inscribirás. Es importante que selecciones esos programas de manera que disminuyas el riesgo de no quedarte en ninguno; recomiendo que lo hagas de la siguiente manera: 2 de ellos pueden ser libres (los dos mejores programas en tu área, por ejemplo), otros 2 pueden ser programas que no sean tan competitivos y dos opciones “seguras”. La dificultad radica en cómo evaluar qué tan probable es recibir admisión, en ese caso, lo mejor es hablar con un <a href="https://educationusa.state.gov/centers/educationusa-advising-center-comexus">experto</a> que pueda evaluar tu perfil.</p>
<h2 id="comentarios-finales">Comentarios finales</h2>
<p>Elegir los programas a los que aplicarás no es sencillo, así que dedícale suficiente tiempo. En la siguiente parte hablaré de las becas y créditos disponibles así como otras opciones para financiar tu programa. Si tienes alguna duda o comentario, no duden en escribirme por Twitter <a href="http://twitter.com/edublancas/">@edublancas</a> o por correo electrónico <a href="mailto:edu@blancas.io">edu@blancas.io</a>.</p>Eduardo Blancasedu@blancas.ioEn esta segunda parte escribiré sobre qué parámetros tomar en cuenta para elegir los programas a los que aplicarás. Considera unos 10-15 programas en tu búsqueda, para finalmente aplicar a unos 6. Lo más importante es la calidad del programa pero considera que entre más prestigio tenga, más competitiva será la admisión, el resto de los aspectos que mencionaré no están en ningún orden particular, dependerá de ti cuál consideres más importante.Aplicando a una maestría en EE.UU. (I): Planeando tu aplicación2019-03-30T00:00:00+00:002019-03-30T00:00:00+00:00https://blancas.io/blog/study-us-i<p>Para inaugurar mi blog, he decidido escribir una serie de artículos para aquellos interesados en entrar a algún programa competitivo de maestría en EE.UU. en el área de STEM. Esta serie de artículos contendrá información que fui recopilando de diversas fuentes cuando me encontraba en el proceso, pero también cosas que tuve que aprender en el camino (y que me hubiera sido muy útil saber desde el principio).</p>
<p>Toma en cuenta que estos artículos se basan únicamente en mi experiencia y es imposible dar guías paso a paso debido a que cada escuela tiene criterios diferentes. La mayoría de la información de este primer artículo la obtuve de <a href="https://quora.com">Quora</a> y <a href="https://magoosh.com">Magoosh</a>, te recomiendo busques recursos más detallados en esas páginas.</p>
<p>En esta primera parte hablaré de qué tomar en cuenta si estás considerando aplicar: ya sea que te encuentres en los primeros años de licenciatura, estés a punto de graduarte o estés a unos meses de comenzar el proceso de aplicación.</p>
<p>Lo primero que hay que mencionar es que todos los aspectos de tu aplicación son importantes y la única forma de mejorar tus posibilidades es tener un perfil competitivo.</p>
<h2 id="si-estás-leyendo-esto-durante-los-primeros-años-de-tu-licenciatura">Si estás leyendo esto durante los primeros años de tu licenciatura</h2>
<p>TL; DR Mantén un promedio arriba de 9.2 e involúcrate en actividades académicas (preferentemente con alguna institución en EE.UU.).</p>
<h3 id="tu-promedio-importa">Tu promedio importa</h3>
<p>Si estás cursando estudios de licenciatura y estás considerando una maestría en EE.UU. en un programa <em>altamente competitivo</em>, tu promedio de licenciatura es algo que debes cuidar. A pesar de que las escuelas indican que “no tienen promedio mínimo requerido”, un promedio bajo puede dejarte fuera (aunque uno alto no garantiza la admisión). En general, considera que un promedio “bueno” para uno de estos programas es de 3.7/4 (equivalente a un 9.2/10). Si tu promedio es menor que eso, no quiere decir que no tienes posibilidades de ser admitido, pero deberás compensarlo en otros aspectos de tu aplicación (con un excelente puntaje en el GRE, por ejemplo).</p>
<p>Dos consideraciones importantes: si provienes de una escuela estricta en sus evaluaciones y algún miembro del comité lo sabe, será un factor que considerán. Es difícil saber qué tan conocida es tu escuela para el comité de admisión, pero puedes investigar si exalumnos de tu escuela se han graduado de los programas de tu interés, o mejor aún, si algun profesor del programa es graduado de tu universidad.</p>
<p>Otro detalle importante es que el comité de admisión dará más importancia a las calificaciones de tu área que a las demás (un 7 te afectara más si fue en cálculo que si fue en literatura).</p>
<h3 id="tus-actividades-fuera-del-salón-también">…tus actividades fuera del salón también</h3>
<p>Una forma de sobresalir entre los aplicantes es demostrar que estás involucrado en tu área fuera del salón de clases. Si tienes oportunidad de involucrarte en proyectos en tu universidad o alguna empresa te puede ayudar mucho (el verano o un semestre de intercambio son buenas formas para lograrlo). Algo que definitivamente puede hacer la diferencia es si estos proyectos los haces en alguna universidad en EE.UU., mejor aún si es en alguna escuela con prestigio en tu área de interés.</p>
<p>Algunos programas de maestría son enfocados en investigación (tienes que escribir una tesis), esto sucede sobre todo en el área de ciencias (en el área de ingeniería los programas suelen ser más aplicados). Si tu programa requiere escribir una tesis, es importante que enfoques tus actividades académicas en cuestiones de investigación (en vez de hacer proyectos aplicados en una empresa, por ejemplo), mucho mejor si estos proyectos culminan en publicaciones científicas.</p>
<h3 id="estudia-inglés">…estudia inglés</h3>
<p>Este es un punto obvio, pero no quiero dejarlo fuera. Si estás en tus primeros años de licenciatura y no puedes <em>hablar</em> en inglés con fluidez, es importante que te pongas a practicar con tiempo pues el examen de inglés que te pedirán (TOEFL iBT) tiene una sección de <em>speaking</em>.</p>
<h3 id="y-si-ya-me-gradué-o-estoy-a-punto-de-graduarme">¿Y si ya me gradué o estoy a punto de graduarme?</h3>
<p>Si ya te graduaste o estás a punto de hacerlo, será más difícicil subir tu promedio o involucrarte en actividades académicas, por lo que tener un buen puntaje en los exámenes (siguiente sección) es muy importante. Si ya tienes algunos años de haberte graduado, tu experiencia laboral (principalmente si es en el área de la maestría) también puede ayudarte.</p>
<h2 id="los-requisitos-para-la-solicitud-de-admisión">Los requisitos para la solicitud de admisión</h2>
<p>TL;DR Obtén un score en el TOEFL mínimo de 100 puntos y un puntaje cuantitativo en el GRE al menos en el percentil 90.</p>
<p>Prácticamente todos los programas a los que apliques van a pedir los mismos requisitos: GRE, TOEFL iBT, <em>résumé</em>, carta de motivos y cartas de recomendación. Es importante que planees cómo cubrir estos requisitos con suficiente tiempo de antelación. Las aplicaciones son en diciembre, te recomiendo que hagas un primer intento de los exámenes, que comiences a trabajar en la carta de motivos y las cartas de recomendación unos 6 meses antes. Puede parecer mucho tiempo, pero hay muchos factores que estarán fuera de tu control (fechas disponibles para tomar los exámenes, ejemplo) y te aseguro que te tomará más tiempo de lo que planees.</p>
<p>Otro aspecto importante es la selección de programas, también te recomiendo comenzar a investigar unos 6 meses antes, la siguiente entrada en esta serie se enfocará en eso. Usualmente los programas no tienen puntajes mínimos en los exámenes pero en algunos casos sí hay (por ejemplo, recuerdo haber visto un programa con puntaje mínimo en la sección de <em>speaking</em> del TOEFL iBT), por eso es importante tener una idea de los programas a los que aplicarás por si alguno tiene requisitos de este tipo.</p>
<p>Existe una cantidad enorme de recursos de cómo preparar tu solicitud, así que seré breve y únicamente incluiré los puntos que considero más importantes.</p>
<h3 id="consejos-para-los-exámenes">Consejos para los exámenes</h3>
<h4 id="toefl-ibt">TOEFL iBT</h4>
<p>Dar consejos respecto al TOEFL es muy difícil dado que depende mucho de qué tan preparado estés unos meses antes de aplicar. Idealmente ya tienes un buen nivel y solo quieres dedicarle un poco de tiempo para tener un mejor puntaje. La mejor forma de tener un buen diagnóstico es hacer el examen una vez. Si alcanzas un puntaje muy bueno (>=110), olvídate del TOEFL y enfócate en el GRE, si tienes un puntaje no tan bueno (menos de 100), considera tomar algún curso para incrementar tu puntaje.</p>
<h4 id="gre">GRE</h4>
<p>En programas técnicos, tu puntaje de GRE es un requisito básico (como tu promedio de licenciatura). Las secciones de <em>verbal reasoning</em> y <em>analytical writing</em> son solo un requisito, mientras tengas un puntaje regular (lo regular depende de cada programa), el comité de admisión probablemente no le dé tanta importancia. El puntaje importante es el de <em>quantitative reasoning</em>. Como regla de dedo considera que un puntaje bueno es estar en o por arriba del percentil 90. El mejor recurso que encontré para estudiar para este examen y obtener estadísticas de qué puntajes se consideran buenos es <a href="https://magoosh.com/">Magoosh</a>. De igual forma, te recomiendo tomar el examen unos 6 meses antes para evaluar tu situación y determinar si es necesario estudiar y volver a tomar el examen.</p>
<h4 id="cómo-prepararme">¿Cómo prepararme?</h4>
<p>El único consejo que puedo dar respecto a cómo prepararse es que practiques ambos exámenes en casa con un formato lo más parecido posible a como será cuando lo tomes de verdad: con cronómetro por sección y usando solo los recursos permitidos. Eso es especialmente importante para el GRE, donde el tiempo por sección es crítico y es necesario que te acostumbres a resolver las preguntas rápido.</p>
<h3 id="résumé"><em>résumé</em></h3>
<p>No te compliques con diseño en tu résumé, busca alguna plantilla para que siga una estructura estándar. Manténlo estrictamente de una cuartilla. Usa <em>bullet points</em>, busca guias de como redactarlos. Es importante que los puntos sean concisos y específicos.</p>
<h3 id="carta-de-motivos">Carta de motivos</h3>
<p>La carta de motivos es la única oportunidad que tienes para convencer al comité de admitirte, debe ser breve y concisa (una cuartilla es un tamaño apropiado). Usa este espacio para hablar de lo que has hecho, de lo que harás en el programa y lo que harás al graduarte. Haz énfasis en las particularidades de tu perfil, los comités de admisión valoran mucho la diversidad en todos los aspectos. Si hay algo en tu aplicación (ejemplo: puntaje bajo en tu licenciatura), aprovecha este espacio para explicar alguna situación extraordinaria que pudo haber afectado tu promedio, si es el caso.</p>
<h3 id="cartas-de-recomendación">Cartas de recomendación</h3>
<p>Los programas usualmente piden 3 cartas de recomendación. Deben ser estrictamente académicas/profesionales. Pidelas únicamente a personas que puedan hablar de tus aptitudes en específico, es preferible tener una carta recomendación de tu profesor de primer año de licenciatura con el que trabajaste en un proyecto a lo largo de un año entero que con el director de tu facultad/escuela que solo sabe tu nombre. Entre más detallada sea la carta, mejor, así que considera personas en las que sepas que se tomarán el tiempo suficiente para escribir una carta muy positiva y detallada. Si estás en tus primeros años de licenciatura, es el momento apropiado de comenzar a acercarte a tus profesores para construir relaciones académicas.</p>
<h2 id="comentarios-finales">Comentarios finales</h2>
<p>En la siguiente parte hablaré de cómo elegir los programas a los que vas a aplicar. ¡hasta la próxima! Si tienes alguna duda o comentario, no duden en escribirme por Twitter <a href="http://twitter.com/edublancas/">@edublancas</a> o por correo electrónico <a href="mailto:edu@blancas.io">edu@blancas.io</a>.</p>Eduardo Blancasedu@blancas.ioPara inaugurar mi blog, he decidido escribir una serie de artículos para aquellos interesados en entrar a algún programa competitivo de maestría en EE.UU. en el área de STEM. Esta serie de artículos contendrá información que fui recopilando de diversas fuentes cuando me encontraba en el proceso, pero también cosas que tuve que aprender en el camino (y que me hubiera sido muy útil saber desde el principio).