<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Evgeny Ivanov</title>
    <subtitle>Blog about data science, decision making, economics, management, and other topics that interest me</subtitle>
    <link rel="self" type="application/atom+xml" href="https://e10v.me/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://e10v.me"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-05-07T00:00:00+00:00</updated>
    <id>https://e10v.me/atom.xml</id>
    <entry xml:lang="en">
        <title>Open source contribution and job search as matching markets under AI-induced congestion</title>
        <published>2026-04-18T00:00:00+00:00</published>
        <updated>2026-05-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Evgeny Ivanov
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://e10v.me/matching-markets-congestion/"/>
        <id>https://e10v.me/matching-markets-congestion/</id>
        
        <content type="html" xml:base="https://e10v.me/matching-markets-congestion/">&lt;p&gt;In &lt;a href=&quot;https:&#x2F;&#x2F;e10v.me&#x2F;prices-as-model-weights&#x2F;&quot;&gt;one of my previous posts&lt;&#x2F;a&gt;, I discussed congestion in the job market caused by the surge of AI tools that scrape job descriptions and auto-apply to jobs. I suggested pay-to-apply as a possible solution, where payment is made with platform-issued virtual points available in limited supply rather than real money. I focused more on explaining why dynamic pricing can solve the problem better than AI screening tools, and I did not go into the details of matching market design.&lt;&#x2F;p&gt;
&lt;p&gt;Since that post, another problem has emerged: progress in the capabilities of coding agents has caused a sharp rise in vibe-coded pull requests in open source repositories on GitHub. This problem can also be framed as a matching market congestion problem.&lt;&#x2F;p&gt;
&lt;p&gt;This problem is not particularly new. It is well researched and described in detail in &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.com&#x2F;Who-Gets-What-Why-Matchmaking&#x2F;dp&#x2F;0544705289&#x2F;&quot;&gt;&quot;Who Gets What―and Why: The New Economics of Matchmaking and Market Design&quot;&lt;&#x2F;a&gt; by Alvin Roth, a winner of the Nobel Prize in Economics.&lt;&#x2F;p&gt;
&lt;p&gt;I became familiar with the problem while working in a services marketplace and &lt;a href=&quot;https:&#x2F;&#x2F;e10v.me&#x2F;ranking-two-sided-matching-platform-part-1&#x2F;&quot;&gt;solving&lt;&#x2F;a&gt; matching-market-related problems there. That gave me direct practical experience with the typical issues. In this post, I want to share that experience and knowledge.&lt;&#x2F;p&gt;
&lt;p&gt;I explore the services marketplace, a dating platform, job search, and open source contribution through the lens of matching market design and identify a common pattern: lowering search and application costs leads to more applications, resulting in less effective matching due to reviewer overload.&lt;&#x2F;p&gt;
&lt;p&gt;I argue that just automating application screening and review with AI doesn&#x27;t fully resolve the problem. In some cases, it makes it even worse by creating a self-reinforcing feedback loop: more applications → more automated filtering → even more applications. AI automation tools lack private information about applicant fit and intent.&lt;&#x2F;p&gt;
&lt;p&gt;As an alternative, I propose to redesign incentives so applicants bear more of the cost of low-value submissions and use their private knowledge to apply more carefully. The proposed solution is a reputation-credit-based system for GitHub-like platforms: non-transferable reputation credits are earned through valuable contributions and debited through low-quality pull requests and issues.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Warning: this is a long read. I wrote it myself and only used AI to check for grammar, punctuation, logical errors, and awkward wording. I really hope you find it interesting and useful.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;matching-markets&quot; class=&quot;h2&quot;&gt;Matching markets&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#matching-markets&quot; aria-label=&quot;Anchor link for: matching-markets&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;difference-between-matching-and-commodity-markets&quot; class=&quot;h4&quot;&gt;Difference between matching and commodity markets&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#difference-between-matching-and-commodity-markets&quot; aria-label=&quot;Anchor link for: difference-between-matching-and-commodity-markets&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A two-sided &lt;em&gt;matching market&lt;&#x2F;em&gt; is a market in which both sides have to make a choice to get what they want, unlike a &lt;em&gt;commodity market&lt;&#x2F;em&gt;, where only one side chooses. Amazon is an example of a commodity marketplace: when a customer places an order, the seller does not get to choose whether to sell to that customer. Examples of matching markets include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Job market.&lt;&#x2F;li&gt;
&lt;li&gt;Dating platforms, like Tinder and Bumble.&lt;&#x2F;li&gt;
&lt;li&gt;Platforms for freelancers, like Upwork and Fiverr.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Generally, turning a matching market into a commodity market increases the number of matches. For example, Airbnb increased the number of bookings on the platform when it introduced the instant book feature, eliminating the host approval step.&lt;&#x2F;p&gt;
&lt;p&gt;Not all matching markets can be effectively turned into commodity markets, because both sides can have unique preferences that can be resolved only through interaction.&lt;&#x2F;p&gt;
&lt;p&gt;For simplicity, I consider only &lt;em&gt;two-sided&lt;&#x2F;em&gt; markets here.&lt;&#x2F;p&gt;
&lt;div class=&quot;accordion mb-3&quot;&gt;
  &lt;div class=&quot;accordion-item&quot;&gt;
    &lt;p class=&quot;accordion-header&quot;&gt;
      &lt;a class=&quot;d-flex collapsed&quot; data-bs-toggle=&quot;collapse&quot; href=&quot;#commodity-market&quot; role=&quot;button&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;commodity-market&quot;&gt;
        Note on the &amp;quot;commodity market&amp;quot; term
        &lt;svg xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; width=&quot;16&quot; height=&quot;16&quot; fill=&quot;currentColor&quot; class=&quot;bi ms-auto&quot; viewBox=&quot;0 0 16 16&quot;&gt;
          &lt;use href=&quot;&#x2F;icons.svg#chevron-down&quot;&gt;&lt;&#x2F;use&gt;
        &lt;&#x2F;svg&gt;
      &lt;&#x2F;a&gt;
    &lt;&#x2F;p&gt;
    &lt;div id=&quot;commodity-market&quot; class=&quot;accordion-collapse collapse&quot;&gt;
      &lt;div class=&quot;accordion-body&quot;&gt;
        &lt;p&gt;The meaning of the term &quot;commodity market&quot; used here may not be standard. I adopted it from &quot;Who Gets What—and Why&quot; by Alvin Roth.&lt;&#x2F;p&gt;

      &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;&lt;h3 id=&quot;optimal-matching-problem-and-market-failure&quot; class=&quot;h4&quot;&gt;Optimal matching problem and market failure&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#optimal-matching-problem-and-market-failure&quot; aria-label=&quot;Anchor link for: optimal-matching-problem-and-market-failure&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In a simplified form, the matching problem can be framed as maximizing the total &lt;em&gt;number of matches&lt;&#x2F;em&gt; given a number of searchers on each side of the market. An example from the job market is maximizing the number of hires given a number of candidates and a number of vacancies.&lt;&#x2F;p&gt;
&lt;p&gt;In reality, not all matches are equal. For example, a candidate&#x27;s satisfaction depends on the job they find, and a firm&#x27;s performance depends on the candidate it hires. So the quality of matches matters. In other words, different matches have different &lt;em&gt;economic value&lt;&#x2F;em&gt; for both sides of a market.&lt;&#x2F;p&gt;
&lt;p&gt;In a more precise form, the matching problem can be framed as maximizing the total value created for both sides of the market by the matches that are formed. When the actual value from formed matches is lower than it could be, I will call it a &lt;em&gt;market failure&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In other words, there are two possible market failure indicators:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Fewer matches are formed than possible.&lt;&#x2F;li&gt;
&lt;li&gt;Average match quality is lower than possible.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;market-friction-and-congestion&quot; class=&quot;h4&quot;&gt;Market friction and congestion&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#market-friction-and-congestion&quot; aria-label=&quot;Anchor link for: market-friction-and-congestion&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;em&gt;Market friction&lt;&#x2F;em&gt; is any obstacle that makes it harder for mutually good matches to form, leading to market failure. That includes search costs, application costs, incomplete information, screening costs, interaction costs, communication delays, and similar barriers.&lt;&#x2F;p&gt;
&lt;p&gt;There is also a more specific &lt;em&gt;congestion&lt;&#x2F;em&gt; problem: there are too many potential interactions to process in the time or attention available. In matching markets, this shows up when hospitals, schools, firms, or users receive more applications, messages, or candidates than they can reasonably evaluate, so some promising opportunities are never screened or are screened too late.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;matching-market-examples&quot; class=&quot;h2&quot;&gt;Matching market examples&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#matching-market-examples&quot; aria-label=&quot;Anchor link for: matching-market-examples&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;services-marketplace&quot; class=&quot;h4&quot;&gt;Services marketplace&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#services-marketplace&quot; aria-label=&quot;Anchor link for: services-marketplace&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;As the first example, let&#x27;s consider a services marketplace: a two-sided matching platform where customers can find professionals, and professionals can find customers.&lt;&#x2F;p&gt;
&lt;p&gt;The matching process:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Customers place orders and describe the details of the work.&lt;&#x2F;li&gt;
&lt;li&gt;Professionals search, choose orders, and send proposals to the customers.&lt;&#x2F;li&gt;
&lt;li&gt;The customers review the proposals and choose the professionals, or do not choose anyone.&lt;&#x2F;li&gt;
&lt;li&gt;If a professional sends a proposal to a customer, and the customer chooses the professional, that results in a deal.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Professionals pay the marketplace for opportunities and deals. Market efficiency heavily depends on the monetization model. Possible options:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Pay-to-apply: a professional pays for each proposal they send to a customer.&lt;&#x2F;li&gt;
&lt;li&gt;Pay-per-contact: a professional pays for a customer&#x27;s phone number, with the customer&#x27;s approval. This contact exchange happens after the professional&#x27;s proposal, the customer&#x27;s review of that proposal, and some interaction between the professional and the customer in the built-in chat.&lt;&#x2F;li&gt;
&lt;li&gt;Pay-per-deal: a professional pays for a deal with a customer, the last step of the funnel.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Pay-to-apply is an example of a matching market application cost, a particular case of market friction. It turns out that reducing this friction by implementing pay-per-contact or pay-per-deal models can create a congestion problem and cause a market failure. How can this happen?&lt;&#x2F;p&gt;
&lt;div class=&quot;accordion mb-3&quot;&gt;
  &lt;div class=&quot;accordion-item&quot;&gt;
    &lt;p class=&quot;accordion-header&quot;&gt;
      &lt;a class=&quot;d-flex collapsed&quot; data-bs-toggle=&quot;collapse&quot; href=&quot;#why-both-sides-choose&quot; role=&quot;button&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;why-both-sides-choose&quot;&gt;
        Why both sides have to choose
        &lt;svg xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; width=&quot;16&quot; height=&quot;16&quot; fill=&quot;currentColor&quot; class=&quot;bi ms-auto&quot; viewBox=&quot;0 0 16 16&quot;&gt;
          &lt;use href=&quot;&#x2F;icons.svg#chevron-down&quot;&gt;&lt;&#x2F;use&gt;
        &lt;&#x2F;svg&gt;
      &lt;&#x2F;a&gt;
    &lt;&#x2F;p&gt;
    &lt;div id=&quot;why-both-sides-choose&quot; class=&quot;accordion-collapse collapse&quot;&gt;
      &lt;div class=&quot;accordion-body&quot;&gt;
        &lt;p&gt;At this point, I want to explain why both sides have to choose in this particular marketplace. It is a horizontal marketplace covering over 900 types of services. Orders cannot be fully formalized, so some preferences on both sides still have to be resolved through interaction between a professional and a customer.&lt;&#x2F;p&gt;

      &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Let&#x27;s consider the step in which professionals search and evaluate orders. Their experience allows them to estimate the likelihood of a deal for each particular order. Careful evaluation before deciding whether to apply can reduce application costs. But evaluation consumes time and cognitive resources. It makes sense only when evaluation costs outweigh the reduction in application costs.&lt;&#x2F;p&gt;
&lt;p&gt;When &lt;em&gt;pay-to-apply&lt;&#x2F;em&gt; is the only monetization model, careful evaluation before sending a proposal to a customer reduces total cost per deal. By cost, I mean time, money, and cognitive resources.&lt;&#x2F;p&gt;
&lt;p&gt;With &lt;em&gt;pay-per-contact&lt;&#x2F;em&gt; or &lt;em&gt;pay-per-deal&lt;&#x2F;em&gt;, the application cost is lower. In that case, it is less rational for professionals to spend time evaluating orders, because it does not save them much: they can always refuse after a short interaction with a customer. Moreover, skipping evaluation helps them be the first to apply, which increases the chances of a deal. So these monetization models create incentives for professionals to send proposals to as many new orders as they can find with minimal evaluation.&lt;&#x2F;p&gt;
&lt;p&gt;This behavior can create a congestion problem:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;There are too many proposals for customers to process.&lt;&#x2F;li&gt;
&lt;li&gt;Some proposals are not considered, including potentially the best matches.&lt;&#x2F;li&gt;
&lt;li&gt;Some deals do not happen. And even when they do, they are not optimal in terms of economic value for either customers or professionals.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There are two ways the marketplace can mitigate this problem:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Use &lt;em&gt;pay-to-apply&lt;&#x2F;em&gt; as the only monetization model.&lt;&#x2F;li&gt;
&lt;li&gt;Implement &lt;em&gt;pay-per-contact&lt;&#x2F;em&gt; and&#x2F;or &lt;em&gt;pay-per-deal&lt;&#x2F;em&gt; with restrictions:
&lt;ul&gt;
&lt;li&gt;Limit the number of proposals a professional can send per unit of time, depending on their individual conversion from proposal to a &quot;useful interaction&quot;.&lt;&#x2F;li&gt;
&lt;li&gt;Limit and&#x2F;or delay the access to the most popular orders, depending on professionals&#x27; individual conversion from proposal to a &quot;useful interaction&quot;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;dating-platform&quot; class=&quot;h4&quot;&gt;Dating platform&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#dating-platform&quot; aria-label=&quot;Anchor link for: dating-platform&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Dating platforms are another example of matching markets. Let&#x27;s consider Tinder:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;People create a profile with photos and a short bio, then browse nearby users.&lt;&#x2F;li&gt;
&lt;li&gt;You swipe right if you’re interested and left if you’re not.&lt;&#x2F;li&gt;
&lt;li&gt;If two people both swipe right on each other, it becomes a match and they can chat in the app.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We will focus on male-female matching there. The market design is symmetrical, but the market balance is not: there are roughly three men for every one woman. The probability of a match is much lower for men. In this example, men are the long side of the market, while women are the short side. The long side can be viewed as the side that searches and applies, and the short side as the side that reviews and approves or rejects.&lt;&#x2F;p&gt;
&lt;p&gt;Under a 3:1 market balance, screening each profile thoroughly before swiping becomes exhausting. This leads to the following behavior:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Many users on the long side swipe right on almost everyone. Only after there is a match do they screen it and decide whether to start a chat.&lt;&#x2F;li&gt;
&lt;li&gt;Users on the short side get overloaded and must spend more effort screening.&lt;&#x2F;li&gt;
&lt;li&gt;As users on the short side face more noise, they become more selective, and the cycle reinforces itself.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is another example of the congestion problem.&lt;&#x2F;p&gt;
&lt;p&gt;Tinder limits the number of right-swipes for free-tier users, but it is unlimited on paid subscriptions.&lt;&#x2F;p&gt;
&lt;p&gt;In the paper &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ssrn.com&#x2F;abstract=3004814&quot;&gt;&quot;Facilitating the Search for Partners on Matching Platforms&quot;&lt;&#x2F;a&gt;, Yash Kanoria and Daniela Saban explain dating platform users&#x27; behavior with a theoretical model. They also propose a market design to deal with the congestion problem: force the short side of the market to initiate contact with potential partners, by disallowing the long side from doing so.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;markets-with-ai-induced-congestion-problem&quot; class=&quot;h2&quot;&gt;Markets with AI-induced congestion problem&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#markets-with-ai-induced-congestion-problem&quot; aria-label=&quot;Anchor link for: markets-with-ai-induced-congestion-problem&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;job-market&quot; class=&quot;h4&quot;&gt;Job market&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#job-market&quot; aria-label=&quot;Anchor link for: job-market&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In the job market, congestion occurs when too many candidates apply for a job. Recruiters&#x27; and hiring managers&#x27; attention is scarce. As a result, some applications are not considered, some are considered too late, and good candidates are rejected.&lt;&#x2F;p&gt;
&lt;p&gt;In some segments, the problem already existed in the pre-AI era. AI tools have taken it to the next level:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Candidates use AI to find and submit more applications faster.&lt;&#x2F;li&gt;
&lt;li&gt;Employers respond with more AI screening.&lt;&#x2F;li&gt;
&lt;li&gt;That creates a self-reinforcing feedback loop.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;AI tools reduce employers&#x27; screening costs, but that can still lead to market failure because of information asymmetry:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;No matter how advanced the AI models are, they lack crucial information about a candidate&#x27;s fit for the job, and that limits their predictive ability.&lt;&#x2F;li&gt;
&lt;li&gt;As a result, good candidates are rejected, including potentially the best fit, and less economic value is created.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A better solution would be to encourage candidates to use their own knowledge of their skills, screen jobs more carefully, and apply more selectively. In other words, we need better market design, not just better tools using the same information.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;open-source-contribution-market&quot; class=&quot;h4&quot;&gt;Open source contribution market&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#open-source-contribution-market&quot; aria-label=&quot;Anchor link for: open-source-contribution-market&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Open source contributions can also be viewed as a matching market with two sides: contributors and projects.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Developers select the projects they want to contribute to, develop new features or fix bugs, and then create pull requests to merge their changes.&lt;&#x2F;li&gt;
&lt;li&gt;Project members screen and review the suggested changes, provide feedback, and then merge or reject them.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Progress in the capabilities of AI coding agents has made it possible to create more contributions. Moreover, these tools have lowered the barrier to entry for new contributors. As a result, the number of pull requests in popular projects has increased, but their average quality has decreased.&lt;&#x2F;p&gt;
&lt;p&gt;In many cases, project members do not have enough capacity to review all incoming pull requests. As a result, some high-quality contributions are not accepted or not even considered, and fewer useful features and fixes are merged. In other words, less value is created, and the market fails.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;common-pattern&quot; class=&quot;h2&quot;&gt;Common pattern&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#common-pattern&quot; aria-label=&quot;Anchor link for: common-pattern&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A common pattern across the examples above:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Lowering search and application costs increases the number of applications.&lt;&#x2F;li&gt;
&lt;li&gt;Reviewers&#x27; capacity is limited, review and interaction costs are high relative to search and application costs.&lt;&#x2F;li&gt;
&lt;li&gt;Some applications are considered with a delay or not considered at all.&lt;&#x2F;li&gt;
&lt;li&gt;As a result, fewer matches are formed and average value per match decreases.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In some cases, there is an effort to increase review capacity by automating part of the process. This is often not very effective and can reinforce the feedback loop: more applications → stricter review → even more applications. The reason is information asymmetry: the reviewing side has limited information and cannot automate selection effectively; a costly interaction with the applicant is often needed to resolve preferences.&lt;&#x2F;p&gt;
&lt;p&gt;From my personal experience in a services marketplace, the following changes in market design can be more effective:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Increasing application cost.&lt;&#x2F;li&gt;
&lt;li&gt;Implementing personalized restrictions based on the applicant&#x27;s &quot;reputation&quot; and the efficiency of previous applications.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The main reason this works better is that applicants are pushed to screen and apply more selectively, using the &lt;em&gt;information available only to them&lt;&#x2F;em&gt;, such as their skills and preferences.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;market-design-proposition&quot; class=&quot;h2&quot;&gt;Market design proposition&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#market-design-proposition&quot; aria-label=&quot;Anchor link for: market-design-proposition&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;goal-and-hypothesis&quot; class=&quot;h4&quot;&gt;Goal and hypothesis&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#goal-and-hypothesis&quot; aria-label=&quot;Anchor link for: goal-and-hypothesis&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In this section, I propose a new design for the open source contribution market. Since most open source projects are hosted on a single platform, GitHub, and most interactions happen there, it is more feasible to implement than a similar design would be in the job market.&lt;&#x2F;p&gt;
&lt;p&gt;The goal is to increase the number of valuable merged contributions while decreasing the time and attention project members spend reviewing low-value pull requests and issues.&lt;&#x2F;p&gt;
&lt;p&gt;The hypothesis is that the suggested market design would drive two evolutionary processes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Mutation: contributors would change their behavior by creating more useful and fewer low-value pull requests and issues.&lt;&#x2F;li&gt;
&lt;li&gt;Selection: low-value contributors unwilling to adapt would be restricted from creating pull requests and issues.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The difference from biological evolution is that mutation is not random.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;rules-and-processes&quot; class=&quot;h4&quot;&gt;Rules and processes&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#rules-and-processes&quot; aria-label=&quot;Anchor link for: rules-and-processes&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;First, let&#x27;s introduce new concepts and rules:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Creating a pull request or an issue requires a &lt;em&gt;deposit&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;The deposit requirement is denominated in &lt;em&gt;reputation credits&lt;&#x2F;em&gt; and set by project owners.&lt;&#x2F;li&gt;
&lt;li&gt;Both contributors and projects have reputation credit balances.&lt;&#x2F;li&gt;
&lt;li&gt;By default, a contributor cannot create a pull request or an issue if their credit balance is below the deposit requirement.&lt;&#x2F;li&gt;
&lt;li&gt;Both sides earn reputation credits through merged pull requests.&lt;&#x2F;li&gt;
&lt;li&gt;Reputation credits can also be provided or loaned by the platform under certain conditions.&lt;&#x2F;li&gt;
&lt;li&gt;Reputation credits cannot be bought or sold in exchange for real-world money.&lt;&#x2F;li&gt;
&lt;li&gt;Reputation credits cannot be transferred between contributors or projects.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Matching process for pull requests:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A contributor creates a pull request.&lt;&#x2F;li&gt;
&lt;li&gt;The deposit for the pull request is reserved from their balance.&lt;&#x2F;li&gt;
&lt;li&gt;Project members review the pull request and interact with the contributor, including through code changes in the pull request.&lt;&#x2F;li&gt;
&lt;li&gt;As a result of interaction, the pull request is either merged or rejected.&lt;&#x2F;li&gt;
&lt;li&gt;If the pull request is merged, the reserved deposit is returned to the contributor&#x27;s balance. Also, additional credits are issued and added to both the contributor&#x27;s and the project&#x27;s balances. The amount is determined by the contributor&#x27;s and the project&#x27;s credit balances.&lt;&#x2F;li&gt;
&lt;li&gt;If the pull request is rejected, the reserved deposit is debited from the contributor&#x27;s balance, but not added to the project&#x27;s balance. As an exception, project members can choose not to debit the contributor&#x27;s balance, or to debit it only partially. This does not affect the project balance in any way.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Matching process for issues is similar, with an exception: additional credits are not added to the contributor or the project balances.&lt;&#x2F;p&gt;
&lt;p&gt;Additional details:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Deposit requirements can differ among projects, they are defined by project members.&lt;&#x2F;li&gt;
&lt;li&gt;In general, deposit requirements differ for creating pull requests and issues.&lt;&#x2F;li&gt;
&lt;li&gt;The deposit requirement for a pull request can have fixed and variable components, for example per &lt;em&gt;net&lt;&#x2F;em&gt; LOC added.&lt;&#x2F;li&gt;
&lt;li&gt;To avoid wrong incentives, credit balances are visible only to their owners.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;possible-issues-and-edge-cases&quot; class=&quot;h4&quot;&gt;Possible issues and edge cases&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#possible-issues-and-edge-cases&quot; aria-label=&quot;Anchor link for: possible-issues-and-edge-cases&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s consider possible issues and edge cases: reputation gaming, system initialization, entry barriers and onboarding for new contributors, and reputation expiration.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;reputation-gaming&quot; class=&quot;h6&quot;&gt;Reputation gaming&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#reputation-gaming&quot; aria-label=&quot;Anchor link for: reputation-gaming&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;The proposed design creates incentives to game reputation credits. For example, one might try to create a fake project and farm new reputation credits by creating and merging pull requests into their own project. The design should protect against this kind of behavior. In particular, this risk should be taken into account in the calculation of new reputation credits. See the Appendix for details.&lt;&#x2F;p&gt;
&lt;p&gt;There will be some breaches anyway. A fully automated, bulletproof design is not possible. But I don&#x27;t think it is required. I rely on the assumption that incentives for reputation gaming are not very strong, and that very few contributors would go to great lengths to earn credits fraudulently. After all, reputation credits are not real money. The design disconnects them from real-world money by making them non-transferable between contributors and projects. Also, credit balances are private by design to avoid the temptation to use them as a vanity metric.&lt;&#x2F;p&gt;
&lt;p&gt;There should probably also be platform-managed arbitration based on human judgment, along with a way to report fraud.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;system-initialization&quot; class=&quot;h6&quot;&gt;System initialization&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#system-initialization&quot; aria-label=&quot;Anchor link for: system-initialization&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;The market design efficiency depends on how projects&#x27; and contributors&#x27; balances are initialized in the beginning. A possible approach is to use projects&#x27; GitHub stars and history of contributions in the pre-AI era.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;entry-barriers-and-onboarding-for-new-contributors&quot; class=&quot;h6&quot;&gt;Entry barriers and onboarding for new contributors&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#entry-barriers-and-onboarding-for-new-contributors&quot; aria-label=&quot;Anchor link for: entry-barriers-and-onboarding-for-new-contributors&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Without special measures, the new design will create very high entry barriers for newcomers. There are several ways to deal with this problem:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The platform can provide a certain amount of credits to newcomers upon registration. To avoid reputation gaming, these credits should not be taken into account when new credits are issued through merged pull requests.&lt;&#x2F;li&gt;
&lt;li&gt;The first several issues and pull requests after registration might not require a deposit. Then the deposit requirement can gradually increase with each new pull request until it reaches the full level set by project owners.&lt;&#x2F;li&gt;
&lt;li&gt;Some projects can allow deposit-free contributions for a limited number of new contributors, selected by their own criteria. Many projects might not require a deposit at all if they have enough capacity to review all incoming pull requests.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I assume that most members of open source projects are interested in pull requests and new contributors. They are not interested in setting barriers to contributing that are too high. Just high enough to filter spam.&lt;&#x2F;p&gt;
&lt;p&gt;Special treatment for newcomers can create an incentive to create new accounts. But I don&#x27;t believe the incentive would be strong. The real goal is not to make a couple of pull requests but to earn a reputation that allows more contributions. Creating a new account doesn&#x27;t really help achieve this goal.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;reputation-expiration&quot; class=&quot;h6&quot;&gt;Reputation expiration&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#reputation-expiration&quot; aria-label=&quot;Anchor link for: reputation-expiration&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Even with special measures for newcomers, the design still favors incumbents who started earlier. Also, contributors who earned a lot of reputation credits over time might start abusing them. To avoid this, earned reputation credits should expire or be discounted over time. As an alternative, there can be a fixed number of the most recent pull requests and issues that are taken into account when calculating a credit balance.&lt;&#x2F;p&gt;
&lt;p&gt;I would also consider a mechanism to forget old low-value contributions and give a second chance to contributors with low reputation balance.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-it-should-work&quot; class=&quot;h4&quot;&gt;Why it should work&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#why-it-should-work&quot; aria-label=&quot;Anchor link for: why-it-should-work&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The deposit requirement plays the same role in balancing supply (contributors) and demand (projects) as prices do in more traditional markets. Project members can use it to adjust the quantity and quality of incoming pull requests and issues.&lt;&#x2F;p&gt;
&lt;p&gt;Popular projects are expected to have higher deposit requirements on average.&lt;&#x2F;p&gt;
&lt;p&gt;The design increases application costs for contributors and pushes them to put more care into pull requests and issues.&lt;&#x2F;p&gt;
&lt;p&gt;Contributors with low-quality pull requests and issues will have lower credit balances and thus be restricted from contributing to popular projects.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusions&quot; class=&quot;h2&quot;&gt;Conclusions&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#conclusions&quot; aria-label=&quot;Anchor link for: conclusions&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In this post, I examine job search and open source contribution as matching markets under congestion: AI tools lower search and application costs, which can increase volume while reducing matching quality.&lt;&#x2F;p&gt;
&lt;p&gt;When submitting an application or pull request becomes cheap, applicants screen and apply less carefully and send more low-value attempts. Because reviewer attention is scarce, some high-value submissions are reviewed too late or not at all.&lt;&#x2F;p&gt;
&lt;p&gt;The relevant solution is not simply better automated application screening, but better market design. It would make applicants bear more of the cost of low-value submissions and reveal more of their private information through selective behavior.&lt;&#x2F;p&gt;
&lt;p&gt;The proposed design relies on a good old economic principle: if something is scarce, use pricing to distribute it. The deposit requirement plays a role similar to price in balancing supply and demand, and reputation credits are the unit in which that price is denominated.&lt;&#x2F;p&gt;
&lt;p&gt;This does not mean that such a system is easy to implement or free of tradeoffs. Reputation gaming, entry barriers, and onboarding of new contributors would all require careful design. I discuss some potential issues and possible solutions above. Still, the broader point remains: when AI creates congestion in a matching market, the right response is often to redesign incentives, not only to automate filtering.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t claim the proposed design is the best among similar alternatives. I don&#x27;t see the point of polishing details based only on theoretical considerations. Judging by my experience in the services marketplace, some initial theoretical premises turn out to be false. A workable design requires experimentation, trial and error in the field.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;resources&quot; class=&quot;h2&quot;&gt;Resources&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#resources&quot; aria-label=&quot;Anchor link for: resources&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Here is a list of resources I find useful for studying matching markets in general and the congestion problem in particular:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.com&#x2F;Who-Gets-What-Why-Matchmaking&#x2F;dp&#x2F;0544705289&#x2F;&quot;&gt;Roth, A. E. (2016). Who Gets What―and Why: The New Economics of Matchmaking and Market Design.&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ssrn.com&#x2F;abstract=3004814&quot;&gt;Kanoria, Y. and Saban, D. (2020). Facilitating the Search for Partners on Matching Platforms.&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Matching_theory&quot;&gt;Matching theory (Wikipedia).&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Stable_matching_theory&quot;&gt;Stable matching theory (Wikipedia).&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;appendix-creation-of-new-reputation-credits&quot; class=&quot;h2&quot;&gt;Appendix. Creation of new reputation credits&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#appendix-creation-of-new-reputation-credits&quot; aria-label=&quot;Anchor link for: appendix-creation-of-new-reputation-credits&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In the proposed market design, the total number of reputation credits in the system increases when value is created through merged pull requests, and decreases when time and attention is wasted on rejected pull requests.&lt;&#x2F;p&gt;
&lt;p&gt;It is worth going into detail on how the additional reputation credits can be calculated. Possible formulas:&lt;&#x2F;p&gt;
&lt;p&gt;$$\Delta Y = \ln(1 + \alpha X)$$
$$\Delta X = \ln(1 + \beta Y)$$&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;$X$ is the number of contributor reputation credits before creation of the pull request. It should include only credits earned by the contributor, not credits provided by the platform, for example to newcomers.&lt;&#x2F;li&gt;
&lt;li&gt;$Y$ is the number of project reputation credits before creation of the pull request.&lt;&#x2F;li&gt;
&lt;li&gt;$\Delta X$ is the number of additional reputation credits added to the contributor balance.&lt;&#x2F;li&gt;
&lt;li&gt;$\Delta Y$ is the number of additional reputation credits added to the project balance.&lt;&#x2F;li&gt;
&lt;li&gt;$\alpha$ and $\beta$ are small positive constants managed by the platform.&lt;&#x2F;li&gt;
&lt;li&gt;$\ln$ is the natural logarithm function.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Possible alterations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Use an additional multiplier designating the amount of work done in a pull request. It should probably be kept secret to avoid reputation gaming.&lt;&#x2F;li&gt;
&lt;li&gt;Replace $\ln(1 + \alpha X)$ and $\ln(1 + \beta Y)$ with other increasing concave functions of $X$ and $Y$ respectively.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Important properties:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The higher the project&#x27;s credit balance, the larger the increase in the contributor&#x27;s credits after a pull request is merged, and vice versa. This protects the system from reputation gaming when a contributor creates a fake project and merges useless pull requests just to inflate their credits.&lt;&#x2F;li&gt;
&lt;li&gt;The increasing concave form of the reputation-creation functions at least partially protects the system from excessive inequality, or a &quot;rich get richer&quot; dynamic.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Comparing Python packages for A&#x2F;B test analysis: tea-tasting, Pingouin, statsmodels, and SciPy</title>
        <published>2026-03-01T00:00:00+00:00</published>
        <updated>2026-03-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Evgeny Ivanov
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://e10v.me/python-packages-for-ab-test-analysis/"/>
        <id>https://e10v.me/python-packages-for-ab-test-analysis/</id>
        
        <content type="html" xml:base="https://e10v.me/python-packages-for-ab-test-analysis/">&lt;p&gt;Disclosure: I am also the author of tea-tasting.&lt;&#x2F;p&gt;
&lt;p&gt;This article compares four Python packages that are relevant to A&#x2F;B test analysis: tea-tasting, Pingouin, statsmodels, and SciPy. It does not try to pick a universal winner. Instead, it clarifies what each package does well for common experimentation tasks and how much manual work is needed to produce production-style A&#x2F;B test outputs.&lt;&#x2F;p&gt;
&lt;p&gt;It assumes familiarity with A&#x2F;B testing basics, including randomization, p-values, and confidence intervals.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-b-test-setting-and-analysis-requirements&quot; class=&quot;h2&quot;&gt;A&#x2F;B test setting and analysis requirements&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#a-b-test-setting-and-analysis-requirements&quot; aria-label=&quot;Anchor link for: a-b-test-setting-and-analysis-requirements&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;a-b-tests-in-a-nutshell&quot; class=&quot;h4&quot;&gt;A&#x2F;B tests in a nutshell&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#a-b-tests-in-a-nutshell&quot; aria-label=&quot;Anchor link for: a-b-tests-in-a-nutshell&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;An A&#x2F;B test compares two (or more) variants of a product change by randomly assigning experimental units to variants and measuring outcomes. In online experiments, the randomization unit is usually the user, and the standard assumption is that units are independent.&lt;&#x2F;p&gt;
&lt;p&gt;A typical workflow is:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Design the experiment: choose the randomization unit (usually users), define the target population, and estimate sample size and duration with power analysis.&lt;&#x2F;li&gt;
&lt;li&gt;Run the experiment: ship the treatment, randomize traffic, and collect data.&lt;&#x2F;li&gt;
&lt;li&gt;Analyze and interpret results: compute control and treatment metric values, estimate effects with confidence intervals, and report p-values.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Good references for this mindset include books and papers by Ron Kohavi and Alex Deng, especially on trustworthy experimentation, delta-method metrics, and CUPED.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;typical-metric-types-and-tests&quot; class=&quot;h4&quot;&gt;Typical metric types and tests&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#typical-metric-types-and-tests&quot; aria-label=&quot;Anchor link for: typical-metric-types-and-tests&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The table below summarizes common A&#x2F;B test metric families and the tests usually applied.&lt;&#x2F;p&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;
  &lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Metric type&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Examples&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Typical test&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Average&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Average revenue per user, average orders per user&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Welch&#x27;s t-test (unequal-variance two-sample t-test)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Ratio of averages&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Average revenue per order, average orders per session&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Welch&#x27;s t-test with variance from the delta method&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Proportion&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Proportion of users with at least one order&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Asymptotic tests (z-test, G-test, Pearson&#x27;s chi-squared) or exact tests (Boschloo, Barnard, Fisher)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;figcaption class=&quot;figure-caption&quot;&gt;Typical metric types and tests&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;&lt;h3 id=&quot;typical-per-metric-analysis-output&quot; class=&quot;h4&quot;&gt;Typical per-metric analysis output&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#typical-per-metric-analysis-output&quot; aria-label=&quot;Anchor link for: typical-per-metric-analysis-output&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;For each metric, analysts usually want the same core fields:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Metric value in control.&lt;&#x2F;li&gt;
&lt;li&gt;Metric value in treatment.&lt;&#x2F;li&gt;
&lt;li&gt;Effect size estimate and confidence interval (absolute and&#x2F;or relative).&lt;&#x2F;li&gt;
&lt;li&gt;P-value.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This output format is what makes A&#x2F;B test analysis convenient for repeated use across many experiments.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-b-testing-specifics&quot; class=&quot;h4&quot;&gt;A&#x2F;B testing specifics&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#a-b-testing-specifics&quot; aria-label=&quot;Anchor link for: a-b-testing-specifics&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Some details matter a lot in real experimentation workflows.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Relative effect size is often easier to interpret than absolute effect size. A common mistake is to divide an absolute confidence interval by the control mean. That does not generally produce a valid confidence interval for a relative effect. Use the delta method or Fieller&#x27;s theorem instead (both are standard approaches for ratio-style uncertainty).&lt;&#x2F;li&gt;
&lt;li&gt;Variance reduction (especially CUPED, which uses pre-experiment covariates) is widely used to increase power. CUPED for ratio metrics is more subtle because it combines variance reduction with ratio estimators and delta-method variance.&lt;&#x2F;li&gt;
&lt;li&gt;Multiple hypothesis testing correction becomes important when you track many metrics or compare multiple variants. In practice, teams usually need a clear FWER&#x2F;FDR policy and adjusted p-values (or adjusted significance thresholds) in experiment reports.&lt;&#x2F;li&gt;
&lt;li&gt;Efficiency matters. Teams often analyze many metrics across many experiments. For many tests, only aggregated statistics are required (count, mean, variance, covariance). In those cases, it is usually more efficient to compute aggregates in the data backend and send only summary statistics to Python. A convenience layer for aggregate fetching can reduce both code and latency.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;example-data-setup&quot; class=&quot;h2&quot;&gt;Example data setup&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#example-data-setup&quot; aria-label=&quot;Anchor link for: example-data-setup&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;With that context, the examples below use the same synthetic experiment dataset generated with tea-tasting. They intentionally do not use variance reduction (no CUPED), so the package comparisons stay focused on baseline analysis.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; tea_tasting&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; tt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;make_users_data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;return_type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;pandas&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; rng&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;42&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; n_users&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;5_000&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-begin z-python&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;astype&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-support z-type z-python&quot;&gt;int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;control&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;variant&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;variant&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;print&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;head&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;index&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;False&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Output:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; user  variant  sessions  orders  revenue  has_order&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    0        1         1       1     5.77          1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    1        0         3       0     0.00          0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    2        1         1       0     0.00          0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The metrics used in the examples are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;orders_per_user&lt;&#x2F;code&gt;: average orders per user.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;users_with_orders&lt;&#x2F;code&gt;: proportion of users with at least one order.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;revenue_per_user&lt;&#x2F;code&gt;: average revenue per user.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;revenue_per_order&lt;&#x2F;code&gt;: average revenue per order (ratio of averages).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;All package examples below reuse &lt;code&gt;data&lt;&#x2F;code&gt;, &lt;code&gt;control&lt;&#x2F;code&gt;, and &lt;code&gt;treatment&lt;&#x2F;code&gt; from this setup.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;package-by-package-comparison&quot; class=&quot;h2&quot;&gt;Package-by-package comparison&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#package-by-package-comparison&quot; aria-label=&quot;Anchor link for: package-by-package-comparison&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;tea-tasting&quot; class=&quot;h4&quot;&gt;tea-tasting&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#tea-tasting&quot; aria-label=&quot;Anchor link for: tea-tasting&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;tea-tasting&quot;&gt;tea-tasting&lt;&#x2F;a&gt; is a package specifically designed for A&#x2F;B test analysis. It targets experimentation workflows directly, with metrics, relative effects, CUPED, power analysis, and concise experiment-style outputs.&lt;&#x2F;p&gt;
&lt;p&gt;Best for: teams that want an A&#x2F;B-testing-first workflow with minimal glue code.&lt;&#x2F;p&gt;
&lt;p&gt;This is the most compact example among the four packages because it provides A&#x2F;B-specific metric classes and a high-level &lt;code&gt;Experiment&lt;&#x2F;code&gt; API.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;experiment&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Experiment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    orders_per_user&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    users_with_orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Proportion&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; correction&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;False&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    revenue_per_user&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;revenue&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    revenue_per_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;RatioOfMeans&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;revenue&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;result&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; experiment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;analyze&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;print&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Output:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;metric            control treatment rel_effect_size rel_effect_size_ci pvalue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;orders_per_user     0.511     0.556            8.8%      [-0.74%, 19%] 0.0718&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;users_with_orders   0.334     0.352            5.4%       [-2.4%, 14%]  0.181&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;revenue_per_user     5.06      5.64             11%       [0.22%, 24%] 0.0455&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;revenue_per_order    9.91      10.2            2.5%      [-3.0%, 8.3%]  0.389&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A&#x2F;B testing specifics:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Power analysis: built-in (&lt;code&gt;Experiment.solve_power&lt;&#x2F;code&gt; and metric parameters for effect size, relative effect size, and sample size).&lt;&#x2F;li&gt;
&lt;li&gt;Relative effect and confidence interval: first-class output (including relative CI in the printed result).&lt;&#x2F;li&gt;
&lt;li&gt;CUPED: built-in for averages and ratio metrics.&lt;&#x2F;li&gt;
&lt;li&gt;Multiple hypothesis testing correction: built-in (&lt;code&gt;tt.adjust_fdr&lt;&#x2F;code&gt; and &lt;code&gt;tt.adjust_fwer&lt;&#x2F;code&gt;) for experiment results, including FDR and FWER procedures.&lt;&#x2F;li&gt;
&lt;li&gt;Aggregated statistics workflow: built-in support via metrics that expose required aggregates and integration with data backends (e.g., through Ibis-supported engines).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;pingouin&quot; class=&quot;h4&quot;&gt;Pingouin&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#pingouin&quot; aria-label=&quot;Anchor link for: pingouin&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;raphaelvallat&#x2F;pingouin&quot;&gt;Pingouin&lt;&#x2F;a&gt; is a user-friendly statistical package focused on convenient inferential statistics in pandas-centric workflows. It is strong for common tests and effect sizes, but it is not an A&#x2F;B-specific framework.&lt;&#x2F;p&gt;
&lt;p&gt;Best for: quick pandas-based analyses of standard statistical tests.&lt;&#x2F;p&gt;
&lt;p&gt;Pingouin has a convenient t-test interface and a contingency-table chi-squared helper. It does not provide a built-in ratio-of-averages test with delta-method variance, so the &lt;code&gt;revenue_per_order&lt;&#x2F;code&gt; example is omitted.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; pingouin&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; pg&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;orders_test&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; pg&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;ttest&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    correction&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;iloc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;print&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders_per_user: &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;control=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;treatment=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;effect_size=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; -&lt;&#x2F;span&gt;&lt;span&gt; control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;effect_size_ci=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;orders_test&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;CI95&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;pvalue=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;orders_test&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;p_val&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.4f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Output:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;orders_per_user: control=0.511 treatment=0.556 effect_size=0.045 effect_size_ci=[-0.    0.09] pvalue=0.0718&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;_&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; tests&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; pg&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;chi2_independence&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;variant&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    correction&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;False&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pearson&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; tests&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;loc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;tests&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;test&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;pearson&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;iloc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;print&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;users_with_orders: &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;control=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;treatment=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;effect_size=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; -&lt;&#x2F;span&gt;&lt;span&gt; control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;pvalue=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;pearson&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;pval&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.4f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Output:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;users_with_orders: control=0.334 treatment=0.352 effect_size=0.018 pvalue=0.1811&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Notes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;revenue_per_user&lt;&#x2F;code&gt; uses the same &lt;code&gt;pg.ttest(...)&lt;&#x2F;code&gt; pattern as &lt;code&gt;orders_per_user&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;revenue_per_order&lt;&#x2F;code&gt; (ratio of averages) requires manual derivation if you want a statistically correct delta-method analysis.&lt;&#x2F;li&gt;
&lt;li&gt;The proportion example above shows a p-value, but not a built-in A&#x2F;B-style effect CI.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A&#x2F;B testing specifics:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Power analysis: built-in for standard cases (&lt;code&gt;power_ttest&lt;&#x2F;code&gt;, &lt;code&gt;power_ttest2n&lt;&#x2F;code&gt;, &lt;code&gt;power_chi2&lt;&#x2F;code&gt;), but not an A&#x2F;B-specific multi-metric workflow.&lt;&#x2F;li&gt;
&lt;li&gt;Relative effect and confidence interval: mostly manual for A&#x2F;B-style relative lift CIs.&lt;&#x2F;li&gt;
&lt;li&gt;CUPED: no built-in CUPED abstraction. You would implement variance reduction manually (for example, with regression).&lt;&#x2F;li&gt;
&lt;li&gt;Multiple hypothesis testing correction: built-in p-value adjustment via &lt;code&gt;pg.multicomp&lt;&#x2F;code&gt; (for example, Bonferroni, Holm, and FDR methods), but integration into an A&#x2F;B reporting workflow is manual.&lt;&#x2F;li&gt;
&lt;li&gt;Aggregated statistics workflow: mostly expects granular arrays&#x2F;DataFrames; no built-in A&#x2F;B aggregate interface.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;statsmodels&quot; class=&quot;h4&quot;&gt;statsmodels&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#statsmodels&quot; aria-label=&quot;Anchor link for: statsmodels&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;statsmodels&#x2F;statsmodels&quot;&gt;statsmodels&lt;&#x2F;a&gt; is a broad statistical modeling library with strong hypothesis testing, power analysis, and confidence interval utilities. It is less opinionated than an experimentation-specific package, which is a strength if you want building blocks and explicit control.&lt;&#x2F;p&gt;
&lt;p&gt;Best for: analysts who want mature statistical building blocks and are comfortable assembling a workflow.&lt;&#x2F;p&gt;
&lt;p&gt;The example below uses Welch-style t-tests for two average metrics and a risk-ratio test&#x2F;CI for the proportion metric. &lt;code&gt;revenue_per_order&lt;&#x2F;code&gt; is omitted because there is no built-in A&#x2F;B-style ratio-of-averages delta-method helper.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; statsmodels&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;stats&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;proportion&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; import&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-begin z-python&quot;&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    confint_proportions_2indep&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    test_proportions_2indep&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; statsmodels&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;stats&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;weightstats&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; CompareMeans&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; DescrStatsW&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; welch_summary&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable z-parameter z-function z-language z-python&quot;&gt;treatment_series&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-parameters z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable z-parameter z-function z-language z-python&quot;&gt; control_series&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-parameters&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cm&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt; CompareMeans&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;        DescrStatsW&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;treatment_series&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;        DescrStatsW&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;control_series&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    _&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; pvalue&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; cm&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;ttest_ind&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;usevar&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;unequal&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ci_low&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ci_high&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; cm&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;tconfint_diff&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;usevar&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;unequal&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-begin z-python&quot;&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        control_series&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        treatment_series&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        treatment_series&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; -&lt;&#x2F;span&gt;&lt;span&gt; control_series&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ci_low&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ci_high&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        pvalue&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-end z-python&quot;&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; metric&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-begin z-python&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;revenue&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ctrl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; trt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; effect&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ci_low&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ci_high&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; pvalue&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt; welch_summary&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;metric&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;metric&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;    print&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;        f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;metric&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;_per_user: &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;        f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;control=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;ctrl&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; treatment=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;trt&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; effect_size=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;effect&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;        f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;effect_size_ci=[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;ci_low&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;ci_high&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;] pvalue=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;pvalue&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.4f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Output:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;orders_per_user: control=0.511 treatment=0.556 effect_size=0.045 effect_size_ci=[-0.004, 0.094] pvalue=0.0718&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;revenue_per_user: control=5.062 treatment=5.641 effect_size=0.579 effect_size_ci=[0.012, 1.146] pvalue=0.0455&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;count1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-support z-type z-python&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;nobs1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; len&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;count0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-support z-type z-python&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;nobs0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; len&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;prop_test&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt; test_proportions_2indep&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    count1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;count1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    nobs1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;nobs1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    count2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;count0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    nobs2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;nobs0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    compare&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ratio&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    method&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;log&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;prop_ci&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt; confint_proportions_2indep&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    count1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;count1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    nobs1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;nobs1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    count2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;count0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    nobs2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;nobs0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    compare&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ratio&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    method&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;log&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;print&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;users_with_orders: &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;control=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;treatment=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;rel_effect_size=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;prop_test&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;ratio&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;rel_effect_size_ci=[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;prop_ci&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;prop_ci&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;] &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;pvalue=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;prop_test&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;pvalue&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.4f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Output:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;users_with_orders: control=0.334 treatment=0.352 rel_effect_size=0.054 rel_effect_size_ci=[-0.024, 0.138] pvalue=0.1811&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A&#x2F;B testing specifics:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Power analysis: strong built-in support (&lt;code&gt;TTestIndPower&lt;&#x2F;code&gt;, &lt;code&gt;NormalIndPower&lt;&#x2F;code&gt;, &lt;code&gt;GofChisquarePower&lt;&#x2F;code&gt;, and more).&lt;&#x2F;li&gt;
&lt;li&gt;Relative effect and confidence interval: partial. There are built-in options for some cases (for example, risk ratios for proportions), but no general A&#x2F;B-style relative lift CI interface across metric types.&lt;&#x2F;li&gt;
&lt;li&gt;CUPED: no built-in one-call CUPED API, but it is practical to implement manually with regression tooling.&lt;&#x2F;li&gt;
&lt;li&gt;Multiple hypothesis testing correction: strong built-in support (&lt;code&gt;statsmodels.stats.multitest&lt;&#x2F;code&gt;, including &lt;code&gt;multipletests&lt;&#x2F;code&gt; and &lt;code&gt;fdrcorrection&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;Aggregated statistics workflow: partial. Proportion tests work directly from counts&#x2F;sample sizes; other workflows often still require granular arrays or more manual setup.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;scipy&quot; class=&quot;h4&quot;&gt;SciPy&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#scipy&quot; aria-label=&quot;Anchor link for: scipy&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;scipy&#x2F;scipy&quot;&gt;SciPy&lt;&#x2F;a&gt; is a foundational scientific computing and statistics package used directly or indirectly by many higher-level libraries, including the others in this comparison. It provides robust hypothesis tests and exact tests, but it does not provide a high-level A&#x2F;B testing workflow.&lt;&#x2F;p&gt;
&lt;p&gt;Best for: low-level building blocks and custom A&#x2F;B analysis code.&lt;&#x2F;p&gt;
&lt;p&gt;This snippet shows a Welch t-test for &lt;code&gt;orders_per_user&lt;&#x2F;code&gt; and a Pearson chi-squared test for &lt;code&gt;users_with_orders&lt;&#x2F;code&gt;. Exact tests such as Fisher, Barnard, and Boschloo are also available in SciPy.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; numpy&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; scipy&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; stats&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;orders_test&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; stats&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;ttest_ind&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    equal_var&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;False&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;orders_ci&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; orders_test&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;confidence_interval&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;print&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders_per_user: &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;control=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;treatment=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;effect_size=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; -&lt;&#x2F;span&gt;&lt;span&gt; control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;effect_size_ci=[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;orders_ci&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;low&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;orders_ci&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;high&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;] &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;pvalue=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;orders_test&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;pvalue&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.4f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Output:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;orders_per_user: control=0.511 treatment=0.556 effect_size=0.045 effect_size_ci=[-0.004, 0.094] pvalue=0.0718&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;contingency&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;array&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-begin z-python&quot;&gt;    [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-begin z-python&quot;&gt;        [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-begin z-python&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-begin z-python&quot;&gt;        [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-begin z-python&quot;&gt;            (&lt;&#x2F;span&gt;&lt;span&gt;treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-begin z-python&quot;&gt;            (&lt;&#x2F;span&gt;&lt;span&gt;treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-comparison&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-parenthesis z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-end z-python&quot;&gt;        ]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-end z-python&quot;&gt;    ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;chi2_res&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; stats&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;contingency&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;chi2_contingency&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;contingency&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; correction&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;False&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-function&quot;&gt;print&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;users_with_orders: &lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;control=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;treatment=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;effect_size=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;treatment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-arithmetic&quot;&gt; -&lt;&#x2F;span&gt;&lt;span&gt; control&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;has_order&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.3f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;pvalue=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;chi2_res&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;pvalue&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;:.4f&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-character z-format z-placeholder z-other z-python&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Output:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;users_with_orders: control=0.334 treatment=0.352 effect_size=0.018 pvalue=0.1811&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Notes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;revenue_per_user&lt;&#x2F;code&gt; uses the same &lt;code&gt;stats.ttest_ind(..., equal_var=False)&lt;&#x2F;code&gt; pattern as &lt;code&gt;orders_per_user&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;revenue_per_order&lt;&#x2F;code&gt; (ratio of averages) requires manual delta-method implementation.&lt;&#x2F;li&gt;
&lt;li&gt;Relative effect confidence intervals are also manual.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A&#x2F;B testing specifics:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Power analysis: mostly manual in SciPy (or delegated to custom code &#x2F; another package).&lt;&#x2F;li&gt;
&lt;li&gt;Relative effect and confidence interval: manual.&lt;&#x2F;li&gt;
&lt;li&gt;CUPED: manual.&lt;&#x2F;li&gt;
&lt;li&gt;Multiple hypothesis testing correction: partial. SciPy provides &lt;code&gt;scipy.stats.false_discovery_control&lt;&#x2F;code&gt; for BH&#x2F;BY FDR adjustment, but broader multiple-comparison correction workflows are more limited than in statsmodels.&lt;&#x2F;li&gt;
&lt;li&gt;Aggregated statistics workflow: partial. SciPy supports some summary-statistics and contingency-table tests (for example, &lt;code&gt;ttest_ind_from_stats&lt;&#x2F;code&gt; and chi-squared&#x2F;exact tests on contingency tables), but not an A&#x2F;B-specific aggregate workflow.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;feature-comparison-for-a-b-testing&quot; class=&quot;h2&quot;&gt;Feature comparison for A&#x2F;B testing&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#feature-comparison-for-a-b-testing&quot; aria-label=&quot;Anchor link for: feature-comparison-for-a-b-testing&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;SciPy underpins much of the Python statistics ecosystem. In principle, all the capabilities discussed here can be implemented with NumPy + SciPy plus custom code. The practical question is convenience and code verbosity. For that reason, the table below uses three labels:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;built-in&lt;&#x2F;code&gt;: directly supported in a way that fits common A&#x2F;B analysis tasks.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;partial&lt;&#x2F;code&gt;: some built-in support exists, but not as a complete or ergonomic A&#x2F;B workflow.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;manual&lt;&#x2F;code&gt;: possible, but requires custom implementation&#x2F;glue code.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;
  &lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Feature&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;tea-tasting&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Pingouin&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;statsmodels&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;SciPy&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Power analysis to estimate required number of observations&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;manual&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Welch&#x27;s t-test or Student&#x27;s t-test for analysis of averages&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Welch&#x27;s t-test with delta method for analysis of ratios of averages&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;manual&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;manual&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;manual&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Two-sample proportion z-test, G-test, or Pearson&#x27;s chi-squared test&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Relative effect size confidence intervals&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;manual&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;partial&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;manual&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Variance reduction with CUPED for analysis of averages and ratios of averages&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;manual&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;manual&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;manual&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Multiple hypothesis testing correction (FWER&#x2F;FDR p-value adjustment)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;partial&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Working with aggregated statistics instead of granular data&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;built-in&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;manual&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;partial&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;partial&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;figcaption class=&quot;figure-caption&quot;&gt;Feature comparison for A&amp;#x2F;B testing&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;&lt;h2 id=&quot;conclusions&quot; class=&quot;h2&quot;&gt;Conclusions&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#conclusions&quot; aria-label=&quot;Anchor link for: conclusions&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The four packages sit at different levels of abstraction.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;tea-tasting is the most A&#x2F;B-specific option in this group. It is designed around metrics, experiments, relative effects, CUPED, and aggregate-based workflows.&lt;&#x2F;li&gt;
&lt;li&gt;Pingouin is convenient for standard statistical tests and quick analysis in pandas, but A&#x2F;B-specific workflows (especially ratio metrics, relative CIs, and CUPED) are mostly manual.&lt;&#x2F;li&gt;
&lt;li&gt;statsmodels provides strong statistical building blocks and power analysis. It is a good choice when you want explicit control and are willing to assemble an experimentation workflow yourself.&lt;&#x2F;li&gt;
&lt;li&gt;SciPy is the essential foundation. It can support almost everything with enough custom code, but it is the most verbose option for repeated A&#x2F;B test reporting.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you run many experiments with multiple metrics and need consistent outputs, the main differentiator is not just statistical correctness. It is how much A&#x2F;B-specific workflow a package gives you out of the box.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;inclusion-criteria-for-the-comparison&quot; class=&quot;h2&quot;&gt;Inclusion criteria for the comparison&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#inclusion-criteria-for-the-comparison&quot; aria-label=&quot;Anchor link for: inclusion-criteria-for-the-comparison&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;For transparency, here are the minimum criteria I used to decide which packages to include.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Maintained: a recent release (for example, within a year) and recent commits (for example, within two months) reduce the risk of stale APIs and unresolved compatibility issues.&lt;&#x2F;li&gt;
&lt;li&gt;Well documented: a package should have both a user guide and an API reference, because A&#x2F;B testing code is often reused by analysts with different levels of statistical depth.&lt;&#x2F;li&gt;
&lt;li&gt;Used by a community: a practical heuristic is at least 100 GitHub stars. This is not a quality guarantee, but it often means more examples and more edge cases have already been surfaced.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Scope note: This comparison focuses on frequentist A&#x2F;B testing workflows. Bayesian-first experimentation frameworks are out of scope.&lt;&#x2F;p&gt;
&lt;p&gt;Excluded notable packages (and why):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;spotify&#x2F;confidence&quot;&gt;spotify_confidence&lt;&#x2F;a&gt;: excluded because it lacks documentation and has not added significant new features in the last couple of years.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;MobileTeleSystems&#x2F;Ambrosia&quot;&gt;ambrosia&lt;&#x2F;a&gt;: excluded because it has not added new features in the last couple of years, aside from dependency version updates.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Note: The maintenance and documentation notes in this section are assessed as of March 1, 2026.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;resources&quot; class=&quot;h2&quot;&gt;Resources&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#resources&quot; aria-label=&quot;Anchor link for: resources&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The comparison above is based on the public documentation and APIs of the packages as of March 1, 2026. Current stable versions on PyPI at the time of writing:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;&quot;&gt;tea-tasting&lt;&#x2F;a&gt;: 1.12.0, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pypi.org&#x2F;project&#x2F;tea-tasting&#x2F;&quot;&gt;PyPI&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pingouin-stats.org&#x2F;&quot;&gt;Pingouin&lt;&#x2F;a&gt;: 0.6.0, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pypi.org&#x2F;project&#x2F;pingouin&#x2F;&quot;&gt;PyPI&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.statsmodels.org&#x2F;&quot;&gt;statsmodels&lt;&#x2F;a&gt;: 0.14.6, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pypi.org&#x2F;project&#x2F;statsmodels&#x2F;&quot;&gt;PyPI&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.scipy.org&#x2F;doc&#x2F;scipy&#x2F;reference&#x2F;stats.html&quot;&gt;SciPy stats&lt;&#x2F;a&gt;: 1.17.1, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pypi.org&#x2F;project&#x2F;scipy&#x2F;&quot;&gt;PyPI&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A&#x2F;B testing and statistics references:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.cambridge.org&#x2F;core&#x2F;books&#x2F;abs&#x2F;trustworthy-online-controlled-experiments&#x2F;trustworthy-online-controlled-experiments&#x2F;524D7D0FDF6A542F732D047118618645&quot;&gt;Kohavi, R., Tang, D., &amp;amp; Xu, Y. (2020). Trustworthy online controlled experiments: A practical guide to A&#x2F;B testing.&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alexdeng.github.io&#x2F;public&#x2F;files&#x2F;kdd2018-dm.pdf&quot;&gt;Deng, A., Knoblich, U., &amp;amp; Lu, J. (2018). Applying the delta method in metric analytics: A practical guide with novel ideas.&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;exp-platform.com&#x2F;Documents&#x2F;2013-02-CUPED-ImprovingSensitivityOfControlledExperiments.pdf&quot;&gt;Deng, A., Xu, Y., Kohavi, R., &amp;amp; Walker, T. (2013). Improving the sensitivity of online controlled experiments by utilizing pre-experiment data.&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Multiple_comparisons_problem&quot;&gt;Multiple comparisons problem (Wikipedia).&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Prices as model weights: learning from dispersed data</title>
        <published>2025-10-24T00:00:00+00:00</published>
        <updated>2025-10-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Evgeny Ivanov
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://e10v.me/prices-as-model-weights/"/>
        <id>https://e10v.me/prices-as-model-weights/</id>
        
        <content type="html" xml:base="https://e10v.me/prices-as-model-weights/">&lt;p&gt;Market prices are similar to model weights in the sense that they are &quot;fit&quot; through price discovery mechanisms to optimize for equilibrium. The difference between supply and demand is analogous to the gradient in backpropagation.&lt;&#x2F;p&gt;
&lt;p&gt;An important property of prices is that they can be &quot;fit&quot; to &lt;em&gt;distributed&lt;&#x2F;em&gt; and sometimes &lt;em&gt;hidden&lt;&#x2F;em&gt; data. As Friedrich Hayek argued, prices convey &lt;em&gt;dispersed&lt;&#x2F;em&gt; knowledge that no single person or central planner could ever fully possess. There are problems that deal with information that cannot be accessed directly. A well-designed price discovery mechanism can solve such problems more efficiently than an AI&#x2F;ML model with limited information.&lt;&#x2F;p&gt;
&lt;p&gt;This analogy came to mind while I was considering job market congestion that occurs when too many candidates apply for a role. Most of those candidates are not a good fit for the job, but they still try their luck since it requires little effort from them. As a solution, employers automate screening with AI. Candidates also use AI to scrape job descriptions and apply automatically (like &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=76RJVWvNOOQ&quot;&gt;&quot;tinda finger&quot;&lt;&#x2F;a&gt;). This creates a vicious cycle in which nobody wins.&lt;&#x2F;p&gt;
&lt;p&gt;One possible solution is a market design in which candidates pay to apply, with dynamic pricing. Paying to apply may sound provocative and require thoughtful consideration and careful testing. Payments can be made with platform-issued virtual points, available in limited supply. But here, I focus on why price signals may address this problem better than AI-based screening.&lt;&#x2F;p&gt;
&lt;p&gt;The job market is a matching market with information asymmetry, where both sides have unique preferences resolved only through costly interaction. Any AI&#x2F;ML algorithm is only as good as the data, and that data is limited and often inaccurate. Requiring payment to apply would elicit a candidate&#x27;s own estimate of their fit for the job and their true level of interest. These estimates would be reflected in prices, providing a visible signal that guides other candidates considering the role. This approach could reduce congestion, lower screening costs, and help form better matches.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Ladder of questions in analysis</title>
        <published>2025-05-12T00:00:00+00:00</published>
        <updated>2025-05-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Evgeny Ivanov
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://e10v.me/ladder-of-questions-in-analysis/"/>
        <id>https://e10v.me/ladder-of-questions-in-analysis/</id>
        
        <content type="html" xml:base="https://e10v.me/ladder-of-questions-in-analysis/">&lt;p&gt;A ladder of questions in analysis:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Which &lt;em&gt;question&lt;&#x2F;em&gt; should we ask to address a decision or a problem?&lt;&#x2F;li&gt;
&lt;li&gt;Which &lt;em&gt;model&lt;&#x2F;em&gt; should we choose to answer the question?&lt;&#x2F;li&gt;
&lt;li&gt;Which &lt;em&gt;assumptions&lt;&#x2F;em&gt; must hold for the model to be valid, and do they hold?&lt;&#x2F;li&gt;
&lt;li&gt;What &lt;em&gt;data&lt;&#x2F;em&gt; do we need to apply the model?&lt;&#x2F;li&gt;
&lt;li&gt;What &lt;em&gt;conclusions&lt;&#x2F;em&gt; can we draw after applying the model?&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;From my personal experience, people often skip steps 1 and 3, which can lead to bad decisions or mediocre solutions.&lt;&#x2F;p&gt;
&lt;p&gt;You probably read this in a statistical or data-analysis context. But I believe the framework applies more broadly.&lt;&#x2F;p&gt;
&lt;p&gt;In statistics, answering a wrong question is sometimes called a Type III error. I&#x27;ve already &lt;a href=&quot;https:&#x2F;&#x2F;e10v.me&#x2F;ranking-two-sided-matching-platform-part-1&#x2F;#p-s-type-iii-error&quot;&gt;mentioned&lt;&#x2F;a&gt; it in a blog post.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Curious facts about Ronald Fisher</title>
        <published>2024-08-12T00:00:00+00:00</published>
        <updated>2024-08-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Evgeny Ivanov
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://e10v.me/curious-facts-about-ronald-fisher/"/>
        <id>https://e10v.me/curious-facts-about-ronald-fisher/</id>
        
        <content type="html" xml:base="https://e10v.me/curious-facts-about-ronald-fisher/">&lt;h2 id=&quot;fisher-and-smoking&quot; class=&quot;h2&quot;&gt;Fisher and smoking&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#fisher-and-smoking&quot; aria-label=&quot;Anchor link for: fisher-and-smoking&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Ronald_Fisher&quot;&gt;Ronald Fisher&lt;&#x2F;a&gt;, the father of modern statistics and experimental design, refused to believe that smoking tobacco caused lung cancer. He argued that correlation does not imply causation and even published a paper in Nature titled &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.nature.com&#x2F;articles&#x2F;182596a0&quot;&gt;Cancer and Smoking&lt;&#x2F;a&gt;, defending this viewpoint.&lt;&#x2F;p&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;fisher-smoking.png&quot; class=&quot;figure-img img-fluid me-5 mt-3&quot; alt=&quot;Ronald Fisher enjoying a pipe&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Ronald Fisher enjoying a pipe, 1956. &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.researchgate.net&#x2F;figure&#x2F;RA-Fisher-smoking-a-pipe-1956_fig1_8524006&quot;&gt;Source&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;&lt;h2 id=&quot;fisher-and-guinness&quot; class=&quot;h2&quot;&gt;Fisher and Guinness&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#fisher-and-guinness&quot; aria-label=&quot;Anchor link for: fisher-and-guinness&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Ronald Fisher helped &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;William_Sealy_Gosset&quot;&gt;William Gosset&lt;&#x2F;a&gt; develop a statistical test known as &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Student%27s_t-test&quot;&gt;Student&#x27;s t-test&lt;&#x2F;a&gt;. They exchanged over 150 letters between 1912 and 1934. Gosset published his work under the pen name Student as he was the Head Experimental Brewer at &lt;em&gt;Guinness&lt;&#x2F;em&gt; Brewery and was not allowed to publish under his real name.&lt;&#x2F;p&gt;
&lt;p&gt;The fact about Gosset is well known. Less well known is that Fisher&#x27;s wife, Ruth Eileen Fisher née Guinness, was a great-great-granddaughter of Arthur Guinness, the founder of Guinness Brewery.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fisher-and-a-lady&quot; class=&quot;h2&quot;&gt;Fisher and a lady&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#fisher-and-a-lady&quot; aria-label=&quot;Anchor link for: fisher-and-a-lady&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Ronald Fisher devised the famous &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Lady_tasting_tea&quot;&gt;&quot;lady tasting tea&quot;&lt;&#x2F;a&gt; experiment. In this experiment, he developed the null hypothesis significance testing framework to analyze a lady&#x27;s claim that she could discern whether the tea or the milk was added first to the cup.&lt;&#x2F;p&gt;
&lt;p&gt;The lady was &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Muriel_Bristol&quot;&gt;Muriel Bristol&lt;&#x2F;a&gt;, a phycologist at Rothamsted Research. One day at Rothamsted, Fisher offered Bristol a cup of tea, but she declined, explaining that she preferred the flavor when the milk was poured into the cup &lt;em&gt;before&lt;&#x2F;em&gt; the tea. Fisher said that the order of pouring could not affect the flavor. Bristol insisted that it did and that she could tell the difference. Overhearing this debate, William Roach said, &quot;Let&#x27;s test her.&quot; They devised an experiment, and Lady Bristol proved her case.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fisher-and-tea-tasting&quot; class=&quot;h2&quot;&gt;Fisher and tea-tasting&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#fisher-and-tea-tasting&quot; aria-label=&quot;Anchor link for: fisher-and-tea-tasting&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;tea-tasting&quot;&gt;tea-tasting&lt;&#x2F;a&gt; is a Python package that I developed for the statistical analysis of A&#x2F;B tests. The name &quot;tea-tasting&quot; is a play on words referring to two subjects:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The &quot;lady tasting tea&quot; experiment, devised by Ronald Fisher.&lt;&#x2F;li&gt;
&lt;li&gt;&quot;Tea-tasting&quot; phonetically resembling &quot;t-testing&quot; or Student&#x27;s t-test, developed by William Gosset with the help of Ronald Fisher.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The funny part is that tea-tasting doesn&#x27;t include &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Fisher%27s_exact_test&quot;&gt;Fisher&#x27;s exact test&lt;&#x2F;a&gt;, which was used in the &quot;lady tasting tea&quot; experiment. It&#x27;s like naming a coffee shop &quot;Espresso Corner&quot; and not serving espresso.&lt;&#x2F;p&gt;
&lt;p&gt;Do you think I should implement Fisher&#x27;s exact test in tea-tasting? Or &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Boschloo%27s_test&quot;&gt;Boschloo&#x27;s test&lt;&#x2F;a&gt;? Or &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Barnard%27s_test&quot;&gt;Barnard&#x27;s test&lt;&#x2F;a&gt;? Let me know in the comments below.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>tea-tasting: a Python package for the statistical analysis of A&#x2F;B tests</title>
        <published>2024-07-29T00:00:00+00:00</published>
        <updated>2026-03-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Evgeny Ivanov
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://e10v.me/tea-tasting-analysis-of-experiments/"/>
        <id>https://e10v.me/tea-tasting-analysis-of-experiments/</id>
        
        <content type="html" xml:base="https://e10v.me/tea-tasting-analysis-of-experiments/">&lt;p&gt;&lt;em&gt;Note: the post has been updated on March 7, 2026. It&#x27;s often referred to by AI chatbots and agents without recognizing that some information could be outdated. So I decided to update it.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I developed &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;tea-tasting&quot;&gt;tea-tasting&lt;&#x2F;a&gt;, a Python package for the statistical analysis of A&#x2F;B tests featuring:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Welch&#x27;s t-test, Student&#x27;s t-test, z-test, Bootstrap, variance reduction with CUPED, delta method for ratio metrics, power analysis, and other A&#x2F;B-test-specific statistical methods and approaches out of the box.&lt;&#x2F;li&gt;
&lt;li&gt;Support for a wide range of data backends, such as BigQuery, ClickHouse, PostgreSQL, Snowflake, Trino, and other backends supported by &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ibis-project&#x2F;ibis&quot;&gt;Ibis&lt;&#x2F;a&gt;. tea-tasting also accepts dataframes supported by &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;narwhals-dev&#x2F;narwhals&quot;&gt;Narwhals&lt;&#x2F;a&gt;: cuDF, Daft, Dask, DuckDB, Modin, pandas, Polars, PyArrow, PySpark.&lt;&#x2F;li&gt;
&lt;li&gt;Convenient API for reducing manual work, and a framework for minimizing errors. Extensible API: define custom metrics and use statistical tests of your choice.&lt;&#x2F;li&gt;
&lt;li&gt;Pretty representation of analysis results: rounding to significant digits, rendering in terminals, Jupyter&#x2F;IPython, and marimo notebooks, serialization to Markdown, and conversion to pandas and Polars DataFrames.&lt;&#x2F;li&gt;
&lt;li&gt;Detailed documentation.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In this blog post, I explore each of these advantages of using tea-tasting in the analysis of experiments.&lt;&#x2F;p&gt;
&lt;p&gt;If you are eager to try it, check the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;user-guide&#x2F;&quot;&gt;user guide&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;statistical-methods&quot; class=&quot;h2&quot;&gt;Statistical methods&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#statistical-methods&quot; aria-label=&quot;Anchor link for: statistical-methods&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;tea-tasting includes statistical methods and techniques that cover most of what you might need in the analysis of experiments.&lt;&#x2F;p&gt;
&lt;p&gt;Analyze metric averages with t-test or z-test, proportions with asymptotic or exact tests, ranks with the Mann-Whitney U test, or use Bootstrap to analyze any other statistic of your choice. There is also a predefined method for analyzing quantiles with Bootstrap. tea-tasting detects mismatches in the sample ratios of different variants of an A&#x2F;B test. You can also define a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;custom-metrics&#x2F;&quot;&gt;custom metric&lt;&#x2F;a&gt; with a statistical test of your choice.&lt;&#x2F;p&gt;
&lt;p&gt;tea-tasting applies the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alexdeng.github.io&#x2F;public&#x2F;files&#x2F;kdd2018-dm.pdf&quot;&gt;delta method&lt;&#x2F;a&gt; for the analysis of ratios of averages. For example, the average number of orders per average number of sessions, assuming that sessions are not randomization units.&lt;&#x2F;p&gt;
&lt;p&gt;Use pre-experiment data, metric forecasts, or other covariates to reduce variance and increase the sensitivity of an experiment. This approach is also known as &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;exp-platform.com&#x2F;Documents&#x2F;2013-02-CUPED-ImprovingSensitivityOfControlledExperiments.pdf&quot;&gt;CUPED&lt;&#x2F;a&gt; or &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doordash.engineering&#x2F;2020&#x2F;06&#x2F;08&#x2F;improving-experimental-power-through-control-using-predictions-as-covariate-cupac&#x2F;&quot;&gt;CUPAC&lt;&#x2F;a&gt;. In tea-tasting, it can be easily combined with the delta method for analyzing ratio metrics as well.&lt;&#x2F;p&gt;
&lt;p&gt;The calculation of confidence intervals for &lt;em&gt;percentage&lt;&#x2F;em&gt; change in t-test and z-test can be tricky. Just taking the confidence interval for &lt;em&gt;absolute&lt;&#x2F;em&gt; change and dividing it by the control average will produce a biased result. tea-tasting applies the delta method to calculate the correct interval.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;power-analysis&#x2F;&quot;&gt;Analyze statistical power&lt;&#x2F;a&gt; for t-test and z-test. There are three possible options:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Calculate the effect size, given statistical power and the total number of observations.&lt;&#x2F;li&gt;
&lt;li&gt;Calculate the total number of observations, given statistical power and the effect size.&lt;&#x2F;li&gt;
&lt;li&gt;Calculate statistical power, given the effect size and the total number of observations.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Other A&#x2F;B-test-specific features of tea-tasting:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Procedures for controlling the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;multiple-testing&#x2F;&quot;&gt;multiple hypothesis testing&lt;&#x2F;a&gt; problem:
&lt;ul&gt;
&lt;li&gt;False discovery rate (FDR): Benjamini-Hochberg and Benjamini-Yekutieli procedures.&lt;&#x2F;li&gt;
&lt;li&gt;Family-wise error rate (FWER): Hochberg&#x27;s step-up and Holm&#x27;s step-down procedures.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;simulated-experiments&#x2F;&quot;&gt;Simulations&lt;&#x2F;a&gt;:
&lt;ul&gt;
&lt;li&gt;A&#x2F;A tests are useful for identifying potential issues before conducting the actual A&#x2F;B test.&lt;&#x2F;li&gt;
&lt;li&gt;Treatment simulations are great for power analysis—especially when you need a specific uplift distribution or when an analytical formula doesn’t exist.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Learn more in the detailed &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;user-guide&#x2F;&quot;&gt;user guide&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;data-backends&quot; class=&quot;h2&quot;&gt;Data backends&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#data-backends&quot; aria-label=&quot;Anchor link for: data-backends&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;There are many different databases and engines for storing and processing experimental data. And in most cases it&#x27;s not efficient to pull the detailed experimental data into a Python environment. Many statistical tests, such as t-test or z-test, require only aggregated data for analysis.&lt;&#x2F;p&gt;
&lt;p&gt;For example, if the raw experimental data are stored in ClickHouse, it&#x27;s faster and more efficient to calculate counts, averages, variances, and covariances directly in ClickHouse rather than fetching granular data and performing aggregations in a Python environment.&lt;&#x2F;p&gt;
&lt;p&gt;Querying all the required statistics manually can be a daunting and error-prone task. For example, analysis of ratio metrics and variance reduction with CUPED require not only the number of rows and variance, but also covariances. But don&#x27;t worry: tea-tasting does all this work for you.&lt;&#x2F;p&gt;
&lt;p&gt;tea-tasting supports Ibis Tables as input data. &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ibis-project&#x2F;ibis&quot;&gt;Ibis&lt;&#x2F;a&gt; is a Python package which serves as a DataFrame API to various data backends. It supports many backends including BigQuery, ClickHouse, PostgreSQL, Snowflake, and Trino. You can write an SQL query, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ibis-project.org&#x2F;how-to&#x2F;extending&#x2F;sql#backend.sql&quot;&gt;wrap&lt;&#x2F;a&gt; it as an Ibis Table, and pass it to tea-tasting.&lt;&#x2F;p&gt;
&lt;p&gt;Keep in mind that tea-tasting assumes that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Data is grouped by randomization units, such as individual users.&lt;&#x2F;li&gt;
&lt;li&gt;There is a column indicating the variant of the A&#x2F;B test (typically labeled as A, B, etc.).&lt;&#x2F;li&gt;
&lt;li&gt;All necessary columns for metric calculations (like the number of orders, revenue, etc.) are included in the table.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Some statistical methods, like Bootstrap, require granular data for the analysis. In this case, tea-tasting fetches the detailed data as well.&lt;&#x2F;p&gt;
&lt;p&gt;tea-tasting also accepts dataframes supported by &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;narwhals-dev&#x2F;narwhals&quot;&gt;Narwhals&lt;&#x2F;a&gt;: cuDF, Daft, Dask, DuckDB, Modin, pandas, Polars, PyArrow, PySpark.&lt;&#x2F;p&gt;
&lt;p&gt;Learn more in the guide on &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;data-backends&#x2F;&quot;&gt;data backends&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;convenient-api&quot; class=&quot;h2&quot;&gt;Convenient API&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#convenient-api&quot; aria-label=&quot;Anchor link for: convenient-api&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;You can perform most of the statistical calculations above using just SciPy. In fact, tea-tasting uses it under the hood. What tea-tasting offers on top is a convenient higher-level API.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s easier to show than to describe. Here is the basic example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; tea_tasting&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; tt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;make_users_data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;rng&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;42&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;experiment&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Experiment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    sessions_per_user&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sessions&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    orders_per_session&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;RatioOfMeans&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sessions&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    orders_per_user&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orders&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    revenue_per_user&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;tt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;revenue&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;result&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; experiment&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;analyze&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;result&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Output:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;metric             control treatment rel_effect_size rel_effect_size_ci pvalue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sessions_per_user     2.00      1.98          -0.66%      [-3.7%, 2.5%]  0.674&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;orders_per_session   0.266     0.289            8.8%      [-0.89%, 19%] 0.0762&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;orders_per_user      0.530     0.573            8.0%       [-2.0%, 19%]  0.118&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;revenue_per_user      5.24      5.73            9.3%       [-2.4%, 22%]  0.123&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The two-stage approach, with separate parametrization and inference, is common in statistical modeling. This separation helps in making the code more modular and easier to understand.&lt;&#x2F;p&gt;
&lt;p&gt;tea-tasting performs calculations that can be tricky and error-prone:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Analysis of ratio metrics with delta method.&lt;&#x2F;li&gt;
&lt;li&gt;Variance reduction with CUPED&#x2F;CUPAC (also in combination with the delta method for ratio metrics).&lt;&#x2F;li&gt;
&lt;li&gt;Calculation of confidence intervals for both absolute and percentage change.&lt;&#x2F;li&gt;
&lt;li&gt;Analysis of statistical power.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It also provides a framework for representing experimental data to avoid errors. Grouping the data by randomization units and including all units in the dataset is important for correct analysis.&lt;&#x2F;p&gt;
&lt;p&gt;Check out the &lt;a href=&quot;https:&#x2F;&#x2F;e10v.me&#x2F;python-packages-for-ab-test-analysis&#x2F;&quot;&gt;comparison post&lt;&#x2F;a&gt; to see how tea-tasting differs from general purpose statistical packages like Pingouin, statsmodels, and SciPy.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pretty-representation-and-formatting&quot; class=&quot;h2&quot;&gt;Pretty representation and formatting&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#pretty-representation-and-formatting&quot; aria-label=&quot;Anchor link for: pretty-representation-and-formatting&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;tea-tasting reduces the work to format and represent the analysis results. It rounds numbers to significant digits and renders the result table automatically based on where you display it:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;In a terminal or Python console, the result is shown as a plain-text table (as in the example above).&lt;&#x2F;li&gt;
&lt;li&gt;In IPython and Jupyter, the result is rendered as an HTML table.&lt;&#x2F;li&gt;
&lt;li&gt;In marimo notebooks, the result is rendered as a table widget.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;a href=&quot;tea-tasting.png&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;tea-tasting.png&quot; class=&quot;figure-img img-fluid me-5 mt-3&quot; alt=&quot;Experiment result rendering in marimo&quot;&gt;&lt;&#x2F;a&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Experiment result rendering in marimo&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;In addition, tea-tasting provides methods to serialize the experiment result. Notable examples:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;to_pandas&lt;&#x2F;code&gt;: Convert the result to a Pandas DataFrame.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;to_polars&lt;&#x2F;code&gt;: Convert the result to a Polars DataFrame.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;to_markdown&lt;&#x2F;code&gt;: Convert the result to a Markdown table.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;to_string&lt;&#x2F;code&gt;: Convert the result to a string (with parameters for output customization).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;documentation&quot; class=&quot;h2&quot;&gt;Documentation&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#documentation&quot; aria-label=&quot;Anchor link for: documentation&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Last but not least: documentation. I believe that good documentation is crucial for tool adoption. That&#x27;s why I wrote several user guides and an API reference.&lt;&#x2F;p&gt;
&lt;p&gt;I recommend starting with the example of basic usage in the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;user-guide&#x2F;&quot;&gt;user guide&lt;&#x2F;a&gt;. Then you can explore specific topics:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;data-backends&#x2F;&quot;&gt;Data backends&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;power-analysis&#x2F;&quot;&gt;Power analysis&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;multiple-testing&#x2F;&quot;&gt;Multiple hypothesis testing&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;custom-metrics&#x2F;&quot;&gt;Custom metrics&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;simulated-experiments&#x2F;&quot;&gt;Simulated experiments&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Use the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;api&#x2F;&quot;&gt;API reference&lt;&#x2F;a&gt; to explore all parameters and detailed information about the functions, classes, and methods available in tea-tasting.&lt;&#x2F;p&gt;
&lt;p&gt;The tea-tasting repository includes &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;tea-tasting&#x2F;tree&#x2F;main&#x2F;examples&quot;&gt;examples&lt;&#x2F;a&gt; as copies of the guides in the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;marimo-team&#x2F;marimo&quot;&gt;marimo&lt;&#x2F;a&gt; notebook format. See the corresponding &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;tea-tasting&#x2F;blob&#x2F;main&#x2F;examples&#x2F;README.md&quot;&gt;README&lt;&#x2F;a&gt; to learn how to run them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusions&quot; class=&quot;h2&quot;&gt;Conclusions&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#conclusions&quot; aria-label=&quot;Anchor link for: conclusions&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;There are a variety of statistical methods that can be applied in the analysis of an experiment. But only a handful of them are actually used in most cases.&lt;&#x2F;p&gt;
&lt;p&gt;On the other hand, there are methods specific to the analysis of A&#x2F;B tests that are not included in the general purpose statistical packages like SciPy.&lt;&#x2F;p&gt;
&lt;p&gt;tea-tasting&#x27;s functionality includes the most important statistical tests, as well as methods specific to the analysis of A&#x2F;B tests.&lt;&#x2F;p&gt;
&lt;p&gt;tea-tasting provides a convenient API that helps to reduce the time spent on analysis and minimize the probability of error.&lt;&#x2F;p&gt;
&lt;p&gt;In addition, tea-tasting optimizes computational efficiency by calculating the statistics in the data backend of your choice, where the data are stored.&lt;&#x2F;p&gt;
&lt;p&gt;With the detailed &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tea-tasting.e10v.me&#x2F;&quot;&gt;documentation&lt;&#x2F;a&gt;, you can quickly learn how to use tea-tasting for the analysis of your experiments.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;p-s-package-name&quot; class=&quot;h2&quot;&gt;P.S. Package name&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#p-s-package-name&quot; aria-label=&quot;Anchor link for: p-s-package-name&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The package name &quot;tea-tasting&quot; is a play on words that refers to two subjects:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Lady_tasting_tea&quot;&gt;Lady tasting tea&lt;&#x2F;a&gt; is a famous experiment which was devised by Ronald Fisher. In this experiment, Fisher developed the null hypothesis significance testing framework to analyze a lady&#x27;s claim that she could discern whether the tea or the milk was added first to the cup.&lt;&#x2F;li&gt;
&lt;li&gt;&quot;tea-tasting&quot; phonetically resembles &quot;t-testing&quot; or Student&#x27;s t-test, a statistical test developed by William Gosset.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>This meeting could have been</title>
        <published>2024-06-05T00:00:00+00:00</published>
        <updated>2024-06-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Evgeny Ivanov
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://e10v.me/this-meeting-could-have-been/"/>
        <id>https://e10v.me/this-meeting-could-have-been/</id>
        
        <content type="html" xml:base="https://e10v.me/this-meeting-could-have-been/">&lt;p&gt;&lt;del&gt;an email&lt;&#x2F;del&gt;&lt;br&gt;
&lt;del&gt;a Slack message&lt;&#x2F;del&gt;&lt;br&gt;
&lt;del&gt;a PRD&lt;&#x2F;del&gt;&lt;br&gt;
&lt;del&gt;a one-pager&lt;&#x2F;del&gt;&lt;br&gt;
nothing&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Readme Driven Development for an A&#x2F;B testing analysis package</title>
        <published>2023-12-11T00:00:00+00:00</published>
        <updated>2023-12-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Evgeny Ivanov
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://e10v.me/tea-tasting-rdd/"/>
        <id>https://e10v.me/tea-tasting-rdd/</id>
        
        <content type="html" xml:base="https://e10v.me/tea-tasting-rdd/">&lt;h2 id=&quot;tea-tasting-statistical-analysis-of-a-b-tests&quot; class=&quot;h2&quot;&gt;tea-tasting: statistical analysis of A&#x2F;B tests&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#tea-tasting-statistical-analysis-of-a-b-tests&quot; aria-label=&quot;Anchor link for: tea-tasting-statistical-analysis-of-a-b-tests&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;tea-tasting is a Python package for statistical analysis of A&#x2F;B tests that features:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Student&#x27;s t-test, Z-test, and Bootstrap out of the box.&lt;&#x2F;li&gt;
&lt;li&gt;Extensible API: Define and use statistical tests of your choice.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;alexdeng.github.io&#x2F;public&#x2F;files&#x2F;kdd2018-dm.pdf&quot;&gt;Delta method&lt;&#x2F;a&gt; for ratio metrics.&lt;&#x2F;li&gt;
&lt;li&gt;Variance reduction with &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;exp-platform.com&#x2F;Documents&#x2F;2013-02-CUPED-ImprovingSensitivityOfControlledExperiments.pdf&quot;&gt;CUPED&lt;&#x2F;a&gt;&#x2F;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doordash.engineering&#x2F;2020&#x2F;06&#x2F;08&#x2F;improving-experimental-power-through-control-using-predictions-as-covariate-cupac&#x2F;&quot;&gt;CUPAC&lt;&#x2F;a&gt; (also in combination with delta method for ratio metrics).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Fieller%27s_theorem&quot;&gt;Fieller&#x27;s confidence interval&lt;&#x2F;a&gt; for percent change.&lt;&#x2F;li&gt;
&lt;li&gt;Sample ratio mismatch check.&lt;&#x2F;li&gt;
&lt;li&gt;Power analysis.&lt;&#x2F;li&gt;
&lt;li&gt;A&#x2F;A tests.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Currently, tea-tasting is in the planning stage, and I&#x27;m starting with a README that outlines the proposed API — an approach known as Readme Driven Development (RDD).&lt;&#x2F;p&gt;
&lt;p&gt;In this blog post, I&#x27;ll explain the motivation for creating this package and the benefits of the RDD approach.&lt;&#x2F;p&gt;
&lt;p&gt;Explore the README &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;tea-tasting&#x2F;tree&#x2F;v0.0.1#readme&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I welcome your insights! If you have any suggestions, questions, or ideas for alternative designs, please join the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;tea-tasting&#x2F;discussions&#x2F;6&quot;&gt;conversation&lt;&#x2F;a&gt; on GitHub.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-a-new-package&quot; class=&quot;h2&quot;&gt;Why a new package?&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#why-a-new-package&quot; aria-label=&quot;Anchor link for: why-a-new-package&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;One might wonder why there&#x27;s a need for a new package like tea-tasting when there are already SciPy and statsmodels available. Both of these are general-purpose statistical packages offering a wide range of statistical tests. However, they lack certain methods that are specific to A&#x2F;B test analysis.&lt;&#x2F;p&gt;
&lt;p&gt;tea-tasting aims to fill this gap. For example, it includes methods such as the delta method for ratio metrics, CUPED&#x2F;CUPAC for variance reduction, and methods for calculating confidence intervals for percentage changes.&lt;&#x2F;p&gt;
&lt;p&gt;The intention behind tea-tasting is not only to automate the analysis process but also to reduce the probability of making a mistake. Statistical analysis of A&#x2F;B tests can be challenging, with many opportunities for errors. tea-tasting is designed to mitigate these risks.&lt;&#x2F;p&gt;
&lt;p&gt;Many companies develop in-house platforms for A&#x2F;B testing, but these platforms often don&#x27;t cover every scenario, such as non-standard randomization units used in cluster-level or switchback experiments, or the analysis of new metrics not yet integrated into the platform. tea-tasting is also useful in these cases.&lt;&#x2F;p&gt;
&lt;p&gt;In summary, tea-tasting is designed for a more specialized, error-minimizing approach to A&#x2F;B testing analysis, filling in the gaps left by existing general-purpose statistical packages.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;package-name&quot; class=&quot;h2&quot;&gt;Package name&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#package-name&quot; aria-label=&quot;Anchor link for: package-name&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The package name &quot;tea-tasting&quot; is a play on words that refers to two subjects:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Lady_tasting_tea&quot;&gt;Lady tasting tea&lt;&#x2F;a&gt; is a famous experiment which was devised by Ronald Fisher. In this experiment, Fisher developed the null hypothesis significance testing framework to analyze a lady&#x27;s claim that she could discern whether the tea or the milk was added first to a cup.&lt;&#x2F;li&gt;
&lt;li&gt;&quot;tea-tasting&quot; phonetically resembles &quot;t-testing&quot; or Student&#x27;s t-test, a statistical test developed by William Gosset.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;lady-tasting-tea-small.png&quot; class=&quot;figure-img img-fluid me-5 mt-3&quot; alt=&quot;Lady tasting tea by DALL-E&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Lady tasting tea by DALL-E&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;&lt;h2 id=&quot;readme-driven-development&quot; class=&quot;h2&quot;&gt;Readme Driven Development&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#readme-driven-development&quot; aria-label=&quot;Anchor link for: readme-driven-development&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Readme Driven Development (RDD) is a software development approach where you start by writing the README file first, before writing any code. The idea is similar to Amazon&#x27;s &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.allthingsdistributed.com&#x2F;2006&#x2F;11&#x2F;working_backwards.html&quot;&gt;Working Backwards&lt;&#x2F;a&gt; approach, where a press release is drafted &lt;em&gt;before&lt;&#x2F;em&gt; starting a new product.&lt;&#x2F;p&gt;
&lt;p&gt;Writing a README encourages thinking from a user&#x27;s perspective, focusing on how users will interact with the API. It allows for better project planning. You don&#x27;t need to revise the code every time you decide to include something in the public API.&lt;&#x2F;p&gt;
&lt;p&gt;Don&#x27;t confuse RDD with the Waterfall approach. It&#x27;s not about creating exhaustive specifications or detailed documentation upfront. In fact, with tea-tasting, I might have gone a bit deeper into details than usual, but this was a conscious choice to thoroughly plan the package&#x27;s functionality.&lt;&#x2F;p&gt;
&lt;p&gt;To learn more about RDD, I recommend reading Tom Preston-Werner&#x27;s &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tom.preston-werner.com&#x2F;2010&#x2F;08&#x2F;23&#x2F;readme-driven-development&quot;&gt;blog post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;request-for-feedback&quot; class=&quot;h2&quot;&gt;Request for feedback&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#request-for-feedback&quot; aria-label=&quot;Anchor link for: request-for-feedback&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;One of the key benefits of Readme Driven Development (RDD) is the ability to adapt design choices with minimal cost. After reviewing the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;tea-tasting&#x2F;tree&#x2F;v0.0.1#readme&quot;&gt;README&lt;&#x2F;a&gt;, you might have insights that could enhance this project. It could be anything from improved naming conventions and additional features to an entirely different API approach.&lt;&#x2F;p&gt;
&lt;p&gt;Your perspective is valuable! If the concept of this package interests you and you have ideas for its design, I encourage you to &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;tea-tasting&#x2F;discussions&#x2F;6&quot;&gt;share your thoughts&lt;&#x2F;a&gt; on GitHub. Let&#x27;s collaborate to make tea-tasting as effective and user-friendly as possible.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;p-s-can-chatgpt-write-a-python-package&quot; class=&quot;h2&quot;&gt;P.S. Can ChatGPT write a Python package?&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#p-s-can-chatgpt-write-a-python-package&quot; aria-label=&quot;Anchor link for: p-s-can-chatgpt-write-a-python-package&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Tom Preston-Werner wrote his blog post in 2010. There were no LLMs at that time. Thinking about RDD in 2023, I couldn&#x27;t resist asking ChatGPT to write the package code based on README.&lt;&#x2F;p&gt;
&lt;p&gt;Well, not quite yet. ChatGPT resisted performing a large task at once. After I insisted, ChatGPT started implementing it class by class. In the first class, it started hallucinating about the SciPy API. I gave up shortly.&lt;&#x2F;p&gt;
&lt;p&gt;For those interested in how this experiment unfolded, you can view the entire conversation here: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;chat.openai.com&#x2F;share&#x2F;470ba67f-b697-47b8-8590-47ba46ab35dc&quot;&gt;chat&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Debunking the Dunning–Kruger effect with random number simulation</title>
        <published>2023-09-25T00:00:00+00:00</published>
        <updated>2023-09-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Evgeny Ivanov
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://e10v.me/debunking-dunning-kruger-effect/"/>
        <id>https://e10v.me/debunking-dunning-kruger-effect/</id>
        
        <content type="html" xml:base="https://e10v.me/debunking-dunning-kruger-effect/">&lt;h2 id=&quot;intro&quot; class=&quot;h2&quot;&gt;Intro&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#intro&quot; aria-label=&quot;Anchor link for: intro&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Many people associate the Dunning–Kruger effect with the following image:&lt;&#x2F;p&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;dunning-kruger_misinterpret.svg&quot; class=&quot;figure-img img-fluid mb-0&quot; alt=&quot;Misinterpretation of the Dunning–Kruger effect&quot; width=&quot;480px&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Misinterpretation of the Dunning–Kruger effect&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;Actually, this image is a misinterpretation of the conclusions drawn by Dunning and Kruger from their experiments. The Dunning–Kruger effect is a cognitive bias in which people with limited competence in a particular domain overestimate their abilities.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#wikipedia&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Furthermore, evidence suggests that the Dunning and Kruger experiments might not definitively prove that the effect is real. It can be shown that it&#x27;s a statistical artifact and the result of the way data is manipulated.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#nuhfer_2016&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#nuhfer_2017&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#jarry&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; I&#x27;ll illustrate this using a random number simulation, which offers an intuitive and straightforward understanding.&lt;&#x2F;p&gt;
&lt;p&gt;In this post, I&#x27;ll:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Provide details of the experiments and analysis conducted by Dunning and Kruger.&lt;&#x2F;li&gt;
&lt;li&gt;Simulate similar results using random data that do not imply that the effect is real.&lt;&#x2F;li&gt;
&lt;li&gt;Explain why percentiles are not a suitable measure for these experiments.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I also created a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dunning-kruger.streamlit.app&quot;&gt;Streamlit app&lt;&#x2F;a&gt; as a demo for this blog post. Feel free to play with it and check out the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;dunning-kruger&quot;&gt;source code&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dunning-and-kruger-experiments&quot; class=&quot;h2&quot;&gt;Dunning and Kruger experiments&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#dunning_kruger&quot;&gt;5&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#dunning-and-kruger-experiments&quot; aria-label=&quot;Anchor link for: dunning-and-kruger-experiments&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Justin Kruger and David Dunning conducted several experiments (studies), each in one of the domains: humor, logical reasoning, and English grammar. In each experiment, participants—who were Cornell University students—took a test that assessed their knowledge in the domain. After the test, they were asked to assess their ability compared to other Cornell students. To be specific, participants were required to provide percentile rankings on a scale from 0 to 99. In two of the three studies, they were also asked to assess their test performance.&lt;&#x2F;p&gt;
&lt;p&gt;Dunning and Kruger performed the following analyses for each experiment:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Assigned participants percentile ranks based on their test score, with 0 being the lowest score and 99 being the highest score.&lt;&#x2F;li&gt;
&lt;li&gt;Calculated the average perceived ability percentile and found out that it exceeded the average test score percentile (50, by definition).&lt;&#x2F;li&gt;
&lt;li&gt;Grouped participants in quartiles based on their test score.&lt;&#x2F;li&gt;
&lt;li&gt;Calculated the average test score percentile and the average perceived ability percentile for each test score quartile (see the plot below).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;dunning-kruger_study-1.png&quot; class=&quot;figure-img img-fluid my-3&quot; alt=&quot;Perceived ability to recognize humor as a function of actual test performance (Study 1)&quot; width=&quot;480px&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Perceived ability to recognize humor as a function of actual test performance (Study 1)&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;The plot above compares the average test score percentile and the average perceived ability percentile by test score quartile for the humor study. The plots for logical reasoning and English grammar experiments look similar. They also contain the average perceived test score percentiles that are quite close to the average perceived ability percentiles.&lt;&#x2F;p&gt;
&lt;p&gt;The main conclusions that were drawn:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Participants overall overestimated their abilities.&lt;&#x2F;li&gt;
&lt;li&gt;&quot;Incompetent&quot; participants grossly overestimated their ability relative to their peers.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The second conclusion is commonly referred to as the Dunning–Kruger effect. In the rest of the post, I will focus mostly on this conclusion and on the calculations that led to it.&lt;&#x2F;p&gt;
&lt;p&gt;Dunning and Kruger also conducted other experiments that studied participants&#x27; abilities to reassess their competence. I omit the details as they are not the main interest of this post.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;random-number-simulation&quot; class=&quot;h2&quot;&gt;Random number simulation&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#random-number-simulation&quot; aria-label=&quot;Anchor link for: random-number-simulation&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As previously mentioned, these experiments didn&#x27;t prove the Dunning–Kruger effect. To demonstrate this, I&#x27;ll:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Simulate the experiments with random data implying that the effect is not real.&lt;&#x2F;li&gt;
&lt;li&gt;Perform the same calculations as Dunning and Kruger.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;First, let&#x27;s generate test scores and perceived ability as correlated variables. Each point on the plot below represents a participant with a test score on the x-axis and a perceived ability on the y-axis.&lt;&#x2F;p&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;test_score_vs_perceived_ability.svg&quot; class=&quot;figure-img img-fluid mb-0 me-5 mt-3&quot; alt=&quot;Test score vs. perceived ability&quot; width=&quot;480px&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Test score vs. perceived ability&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;Note that the data generation process doesn&#x27;t imply that &quot;incompetent&quot; participants overestimate their abilities.&lt;&#x2F;p&gt;
&lt;p&gt;Next, we&#x27;ll compute the percentiles for participants&#x27; test scores and perceived abilities. This is not exactly how perceived ability percentiles were obtained in the Dunning and Kruger experiment. But this is still valid for our purpose.&lt;&#x2F;p&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;percentiles.svg&quot; class=&quot;figure-img img-fluid mb-0 me-5 mt-3&quot; alt=&quot;Test score percentile vs. perceived ability percentile&quot; width=&quot;480px&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Test score percentile vs. perceived ability percentile&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;Finally, let&#x27;s calculate the average test score percentile and the average perceived ability percentile by test score quartiles.&lt;&#x2F;p&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;test_score_quartiles.svg&quot; class=&quot;figure-img img-fluid mb-0 me-5 mt-3&quot; alt=&quot;Average percentiles by test score quartiles&quot; width=&quot;480px&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Average percentiles by test score quartiles&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;For the bottom quartile, the average perceived ability percentile is higher than the average test score percentile. Feel free to try different values for the random seed, correlation coefficient (below 1), or number of participants in the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dunning-kruger.streamlit.app&quot;&gt;Streamlit app&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You will always get this result for two variables with imperfect correlation. But Dunning and Kruger used this result as evidence that &quot;incompetent&quot; participants overestimated their abilities. They were wrong, as this result is a statistical artifact and the outcome of the way data is manipulated.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, let&#x27;s use the same data to calculate the average test score percentile and the average perceived ability percentile by &lt;em&gt;perceived ability&lt;&#x2F;em&gt; quartiles.&lt;&#x2F;p&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;perceived_ability_quartiles.svg&quot; class=&quot;figure-img img-fluid mb-0 me-5 mt-3&quot; alt=&quot;Average percentiles by perceived ability quartiles&quot; width=&quot;480px&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Average percentiles by perceived ability quartiles&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;Participants from the bottom quartile, on average, have the lowest test scores. For them, the average perceived ability percentile is also lower than the average test score percentile. Doesn&#x27;t this contradict the Dunning–Kruger effect?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bias-and-percentile-measure&quot; class=&quot;h2&quot;&gt;Bias and percentile measure&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#bias-and-percentile-measure&quot; aria-label=&quot;Anchor link for: bias-and-percentile-measure&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In fact, Dunning and Kruger did discuss the regression effect due to imperfect correlation between actual and perceived ability. But they argued that the overestimation they observed was more psychological than artifactual. The main argument was that the magnitude of miscalibration among the bottom quartile was larger than that of the top quartile.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#dunning_kruger&quot;&gt;5&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As you may have noticed, in the simulation, participants overall don&#x27;t overestimate their abilities. This is different from the Dunning and Kruger experiment. I don&#x27;t think it&#x27;s possible to generate a dataset with average perceived ability percentile higher than the average test score percentile without implying that &quot;incompetent&quot; participants grossly overestimate their abilities. The reason is that percentile is a measure with bounded support, meaning it has a finite minimum and maximum.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s take a participant with a test score which corresponds to the 99th percentile. They cannot overestimate their abilities in percentiles. Participants from the top test score quartile can overestimate their abilities by 24 percentiles maximum and 12 percentiles on average. Participants from the bottom test score quartile can overestimate their abilities by 99 percentiles maximum and 87 percentiles on average.&lt;&#x2F;p&gt;
&lt;p&gt;If participants overall overestimate their abilities, then, with high probability, the magnitude of overestimation will be larger among participants with low test scores. And this is a pure mathematical artifact, not a psychological effect. This reasoning applies to any measure with bounded support, not just percentiles. For example, we would get the same results with test scores as a measure.&lt;&#x2F;p&gt;
&lt;p&gt;So, the actual reason for the Dunning and Kruger observation is a combination of two factors:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Participants overall overestimated their abilities.&lt;&#x2F;li&gt;
&lt;li&gt;Percentile is a measure with bounded support.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Also, I encourage you to think about the question: if a person scores the maximum number of points in a test, does this mean that they cannot overestimate their abilities?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot; class=&quot;h2&quot;&gt;Conclusion&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Justin Kruger and David Dunning conducted several experiments and concluded that &quot;incompetent&quot; participants grossly overestimated their ability relative to their peers. This effect is known as the Dunning–Kruger effect.&lt;&#x2F;p&gt;
&lt;p&gt;I reproduced similar results using random number simulation implying that the effect is not real. If participants overall overestimate their abilities, the magnitude of the overestimation among the participants with low scores is explained by the fact that percentile is a measure with bounded support.&lt;&#x2F;p&gt;
&lt;p&gt;The Dunning–Kruger effect, as observed in the original experiments, is a statistical artifact rather than a psychological phenomenon.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t argue about whether the Dunning–Kruger effect is real or not. My intent is to show that the original Dunning and Kruger experiments don&#x27;t prove it.&lt;&#x2F;p&gt;
&lt;p&gt;I created a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dunning-kruger.streamlit.app&quot;&gt;Streamlit app&lt;&#x2F;a&gt; as a demo for this blog post. Feel free to play with it and check out the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;dunning-kruger&quot;&gt;source code&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;references&quot; class=&quot;h2&quot;&gt;References&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#references&quot; aria-label=&quot;Anchor link for: references&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;wikipedia&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Dunning%E2%80%93Kruger_effect&quot;&gt;Dunning–Kruger effect – Wikipedia&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;nuhfer_2016&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;Nuhfer, Edward, Christopher Cogan, Steven Fleisher, Eric Gaze, and Karl Wirth. (2016). Random Number Simulations Reveal How Random Noise Affects the Measurements and Graphical Portrayals of Self-Assessed Competency. Numeracy 9, Iss. 1: Article 4. DOI: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;dx.doi.org&#x2F;10.5038&#x2F;1936-4660.9.1.4&quot;&gt;http:&#x2F;&#x2F;dx.doi.org&#x2F;10.5038&#x2F;1936-4660.9.1.4&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;nuhfer_2017&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;Nuhfer, Edward, Steven Fleisher, Christopher Cogan, Karl Wirth, and Eric Gaze. (2017). How Random Noise and a Graphical Convention Subverted Behavioral Scientists&#x27; Explanations of Self-Assessment Data: Numeracy Underlies Better Alternatives. Numeracy 10, Iss. 1 : Article 4. DOI: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;dx.doi.org&#x2F;10.5038&#x2F;1936-4660.10.1.4&quot;&gt;http:&#x2F;&#x2F;dx.doi.org&#x2F;10.5038&#x2F;1936-4660.10.1.4&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;jarry&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;Jarry, Jonathan. (2020). The Dunning-Kruger Effect Is Probably Not Real. McGill University, Office for Science and Society. Article: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.mcgill.ca&#x2F;oss&#x2F;article&#x2F;critical-thinking&#x2F;dunning-kruger-effect-probably-not-real&quot;&gt;link&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;dunning_kruger&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;5&lt;&#x2F;sup&gt;
&lt;p&gt;Kruger, J., &amp;amp; Dunning, D. (1999). Unskilled and unaware of it: How difficulties in recognizing one&#x27;s own incompetence lead to inflated self-assessments. Journal of Personality and Social Psychology, 77(6), 1121–1134. DOI: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;psycnet.apa.org&#x2F;doi&#x2F;10.1037&#x2F;0022-3514.77.6.1121&quot;&gt;https:&#x2F;&#x2F;doi.org&#x2F;10.1037&#x2F;0022-3514.77.6.1121&lt;&#x2F;a&gt;. Open access: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.researchgate.net&#x2F;publication&#x2F;12688660_Unskilled_and_Unaware_of_It_How_Difficulties_in_Recognizing_One%27s_Own_Incompetence_Lead_to_Inflated_Self-Assessments&quot;&gt;link&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Ranking in a two-sided matching platform (Part 2)</title>
        <published>2023-08-28T00:00:00+00:00</published>
        <updated>2023-08-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Evgeny Ivanov
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://e10v.me/ranking-two-sided-matching-platform-part-2/"/>
        <id>https://e10v.me/ranking-two-sided-matching-platform-part-2/</id>
        
        <content type="html" xml:base="https://e10v.me/ranking-two-sided-matching-platform-part-2/">&lt;h2 id=&quot;recap&quot; class=&quot;h2&quot;&gt;Recap&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#recap&quot; aria-label=&quot;Anchor link for: recap&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This is the second post about the ranking problem in Profi, a services marketplace where customers can find tutors, beauty masters, plumbers, and other professionals, and professionals can find customers.&lt;&#x2F;p&gt;
&lt;p&gt;Here is the short recap of the &lt;a href=&quot;https:&#x2F;&#x2F;e10v.me&#x2F;ranking-two-sided-matching-platform-part-1&#x2F;&quot;&gt;previous post&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;In Profi, professionals search, choose orders, and send proposals to customers. And then the customers review the proposals and choose the professionals, or don&#x27;t choose anyone. If a professional sends a proposal to a customer, and the customer chooses the professional then this is a deal.&lt;&#x2F;li&gt;
&lt;li&gt;The objective was to increase the number of deals by implementing a new order ranking algorithm in the app for professionals.&lt;&#x2F;li&gt;
&lt;li&gt;Ranking orders by the probability of a proposal did increase the number of proposals, but didn&#x27;t increase the number of deals in the marketplace.&lt;&#x2F;li&gt;
&lt;li&gt;Ranking orders by the uplift in the probability of a deal significantly increased the number of deals in the marketplace.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;uplift_in_probability_of_deal.svg&quot; class=&quot;figure-img img-fluid mb-0&quot; alt=&quot;Uplift in the probability of a deal&quot; width=&quot;440px&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Uplift in the probability of a deal&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;In the second post, I&#x27;ll explain the solution. This post is more mathematical. Please bear with me while I go through some details of the model.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mathematical-formulation&quot; class=&quot;h2&quot;&gt;Mathematical formulation&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#mathematical-formulation&quot; aria-label=&quot;Anchor link for: mathematical-formulation&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;First, let&#x27;s express the uplift in the probability of a deal in a mathematical form:&lt;&#x2F;p&gt;
&lt;p&gt;$$P(Y_i = 1 \mid S_{ij} = 1) - P(Y_i = 1 \mid S_{ij} = 0)$$&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;$Y_i$ is an outcome for the order $i$. If there is a deal, then $Y_i = 1$, otherwise $Y_i = 0$.&lt;&#x2F;li&gt;
&lt;li&gt;$S_{ij}$ indicates whether the professional $j$ has seen the order $i$ or not.&lt;&#x2F;li&gt;
&lt;li&gt;$P(Y_i = 1 \mid S_{ij} = 1)$ is the probability of a deal in the order $i$, given that the professional $j$ has seen the order.&lt;&#x2F;li&gt;
&lt;li&gt;$P(Y_i = 1 \mid S_{ij} = 0)$ is the probability of a deal in the order $i$, given that the professional $j$ hasn&#x27;t seen the order.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It seems impossible to solve this problem directly. Hundreds of professionals should see the order for a deal to happen. Estimating the effect of showing an order to a professional would be very noisy.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s decompose the problem by expanding the expression using the law of total probability:&lt;&#x2F;p&gt;
&lt;p&gt;$$P(T_{ij} = 1 \mid S_{ij} = 1)(P(Y_i = 1 \mid T_{ij} = 1) - P(Y_i = 1 \mid T_{ij} = 0))$$&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;$T_{ij}$ indicates whether the professional $j$ has sent a proposal to the customer $i$ or not.&lt;&#x2F;li&gt;
&lt;li&gt;$P(T_{ij} = 1 \mid S_{ij} = 1)$ is the probability that the professional $j$ will send a proposal to the customer $i$, given that the professional has seen the order.&lt;&#x2F;li&gt;
&lt;li&gt;$P(Y_i = 1 \mid T_{ij} = 1)$ is the probability of a deal in the order $i$, given that the professional $j$ has sent a proposal to the customer.&lt;&#x2F;li&gt;
&lt;li&gt;$P(Y_i = 1 \mid T_{ij} = 0)$ is the probability of a deal in the order $i$, given that the professional $j$ hasn&#x27;t sent a proposal to the customer.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;div class=&quot;accordion mb-3&quot;&gt;
  &lt;div class=&quot;accordion-item&quot;&gt;
    &lt;p class=&quot;accordion-header&quot;&gt;
      &lt;a class=&quot;d-flex collapsed&quot; data-bs-toggle=&quot;collapse&quot; href=&quot;#uplift-derivation&quot; role=&quot;button&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;uplift-derivation&quot;&gt;
        Derivation
        &lt;svg xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; width=&quot;16&quot; height=&quot;16&quot; fill=&quot;currentColor&quot; class=&quot;bi ms-auto&quot; viewBox=&quot;0 0 16 16&quot;&gt;
          &lt;use href=&quot;&#x2F;icons.svg#chevron-down&quot;&gt;&lt;&#x2F;use&gt;
        &lt;&#x2F;svg&gt;
      &lt;&#x2F;a&gt;
    &lt;&#x2F;p&gt;
    &lt;div id=&quot;uplift-derivation&quot; class=&quot;accordion-collapse collapse&quot;&gt;
      &lt;div class=&quot;accordion-body&quot;&gt;
        &lt;p&gt;Let&#x27;s expand the left and the right part of the uplift expression:&lt;&#x2F;p&gt;
&lt;p&gt;
$$
\small
\begin{split}
P(Y_i = 1 \mid S_{ij} = 1) &amp; = P(Y_i = 1 \mid T_{ij} = 1)P(T_{ij} = 1 \mid S_{ij} = 1) \\
&amp; \quad + P(Y_i = 1 \mid T_{ij} = 0)P(T_{ij} = 0 \mid S_{ij} = 1) \\
&amp; = P(Y_i = 1 \mid T_{ij} = 1)P(T_{ij} = 1 \mid S_{ij} = 1) \\
&amp; \quad + P(Y_i = 1 \mid T_{ij} = 0)(1 - P(T_{ij} = 1 \mid S_{ij} = 1)) \\
&amp; = P(T_{ij} = 1 \mid S_{ij} = 1)(P(Y_i = 1 \mid T_{ij} = 1) - P(Y_i = 1 \mid T_{ij} = 0)) \\
&amp; \quad + P(Y_i = 1 \mid T_{ij} = 0) \\
\\
P(Y_i = 1 \mid S_{ij} = 0) &amp; = P(Y_i = 1 \mid T_{ij} = 1)P(T_{ij} = 1 \mid S_{ij} = 0) \\
&amp; \quad + P(Y_i = 1 \mid T_{ij} = 0)P(T_{ij} = 0 \mid S_{ij} = 0) \\
&amp; = P(Y_i = 1 \mid T_{ij} = 0) \\
\end{split}
$$
&lt;&#x2F;p&gt;
&lt;p&gt;The last equation takes into account that a professional cannot send a proposal without seeing the order, i.e.:&lt;&#x2F;p&gt;
&lt;p&gt;
$$
\small
\begin{split}
&amp; P(T_{ij} = 1 \mid S_{ij} = 0) = 0 \\
&amp; P(T_{ij} = 0 \mid S_{ij} = 0) = 1
\end{split}
$$
&lt;&#x2F;p&gt;
&lt;p&gt;Now, we can derive that:&lt;&#x2F;p&gt;
&lt;p&gt;
$$
\small
\begin{split}
&amp; P(Y_i = 1 \mid S_{ij} = 1) - P(Y_i = 1 \mid S_{ij} = 0) \\
= P(T_{ij} = 1 \mid S_{ij} = 1)(&amp; P(Y_i = 1 \mid T_{ij} = 1) - P(Y_i = 1 \mid T_{ij} = 0))
\end{split}
$$
&lt;&#x2F;p&gt;
      &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
  &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;We already have a &lt;a href=&quot;https:&#x2F;&#x2F;e10v.me&#x2F;ranking-two-sided-matching-platform-part-1&#x2F;#probability-of-a-proposal&quot;&gt;model&lt;&#x2F;a&gt; that predicts $P(T_{ij} = 1 \mid S_{ij} = 1)$, the probability that the professional $j$ will send a proposal to the customer $i$.&lt;&#x2F;p&gt;
&lt;p&gt;So, now the problem reduces to predicting the uplift in the probability of a deal caused by a proposal from the professional:&lt;&#x2F;p&gt;
&lt;p&gt;$$P(Y_i = 1 \mid T_{ij} = 1) - P(Y_i = 1 \mid T_{ij} = 0)$$&lt;&#x2F;p&gt;
&lt;p&gt;To solve the problem, we&#x27;ll create a model for the probability of a deal in the order. The difference between the probabilities of a deal with and without a proposal from the professional is the uplift we want to predict.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;discrete-choice-model&quot; class=&quot;h2&quot;&gt;Discrete choice model&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#discrete-choice-model&quot; aria-label=&quot;Anchor link for: discrete-choice-model&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A customer can choose one of the professionals who sent a proposal, or not choose anyone.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s model customer&#x27;s choice with the softmax function:&lt;&#x2F;p&gt;
&lt;p&gt;$$P(Y_{ik}=1) = \frac{e^{U_{ik}}}{e^{U_{i0}} + \sum_{j \in J_i} e^{U_{ij}}} = \frac{e^{U_{ik}-U_{i0}}}{1 + \sum_{j \in J_i} e^{U_{ij}-U_{i0}}}$$&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;$Y_{ik}$ indicates whether the customer $i$ chooses the professional $k$ or not.&lt;&#x2F;li&gt;
&lt;li&gt;$P(Y_{ik}=1)$ is the probability that the customer $i$ will choose the professional $k$.&lt;&#x2F;li&gt;
&lt;li&gt;$U_{ij}$ (or $U_{ik}$) is the utility the customer $i$ obtains from choosing the professional $j$ (or $k$).&lt;&#x2F;li&gt;
&lt;li&gt;$U_{i0}$ is the utility the customer $i$ obtains from not choosing anyone.&lt;&#x2F;li&gt;
&lt;li&gt;$J_i$ is a set of professionals who sent a proposal to the customer $i$.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This model assumes no correlation among alternatives. This might not be 100% true. But we decided to proceed with this model:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Given that customers and professionals have unique preferences that only they can discover, this assumption seems realistic.&lt;&#x2F;li&gt;
&lt;li&gt;Taking into account the sequential number of the proposal in the order can capture correlations between proposals.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Now, we can express the probability that the customer will choose one of the professionals who sent a proposal:&lt;&#x2F;p&gt;
&lt;p&gt;$$P(Y_i=1) = \sum_{j \in J_i} P(Y_{ij}=1) = \frac{\sum_{j \in J_i} e^{U_{ij}-U_{i0}}}{1 + \sum_{j \in J_i} e^{U_{ij}-U_{i0}}}$$&lt;&#x2F;p&gt;
&lt;p&gt;Suppose the customer $i$ has received proposals from a set of professionals $J_i$. Then the uplift in the probability of a deal caused by a proposal from the professional $k$ will be:&lt;&#x2F;p&gt;
&lt;p&gt;
$$
\begin{split}
&amp; P(Y_i = 1 \mid T_{ik} = 1) - P(Y_i = 1 \mid T_{ik} = 0) \\
&amp; = \frac{\sum_{j \in J_i} e^{U_{ij}-U_{i0}} + e^{U_{ik}-U_{i0}}}{1 + \sum_{j \in J_i} e^{U_{ij}-U_{i0}} + e^{U_{ik}-U_{i0}}}
- \frac{\sum_{j \in J_i} e^{U_{ij}-U_{i0}}}{1 + \sum_{j \in J_i} e^{U_{ij}-U_{i0}}} \\
&amp; = \frac{e^{U_{ik}-U_{i0}}}{(1 + \sum_{j \in J_i} e^{U_{ij}-U_{i0}})(1 + \sum_{j \in J_i} e^{U_{ij}-U_{i0}} + e^{U_{ik}-U_{i0}})}
\end{split}
$$
&lt;&#x2F;p&gt;
&lt;p&gt;If we can model the utility, we can estimate the uplift in the probability of a deal.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;utility-function&quot; class=&quot;h2&quot;&gt;Utility function&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#utility-function&quot; aria-label=&quot;Anchor link for: utility-function&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The utility a customer obtains from choosing a professional depends on:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;How much time has passed from the moment of the order to the proposal from the professional.&lt;&#x2F;li&gt;
&lt;li&gt;The rating and the number of reviews in the professional&#x27;s profile.&lt;&#x2F;li&gt;
&lt;li&gt;When the customer was last online.&lt;&#x2F;li&gt;
&lt;li&gt;And other factors.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If we represent the utility as a function of these variables, then the parameters of the function can be estimated by minimizing the cross-entropy (maximum likelihood estimation):&lt;&#x2F;p&gt;
&lt;p&gt;
$$
\begin{split}
H(Y, P(Y)) &amp; = -\sum_i (Y_i \log P(Y_i = 1) + (1 - Y_i) \log P(Y_i = 0)) \\
&amp; = \sum_i (\log (1 + \sum_{j \in J_i} e^{U_{ij}-U_{i0}}) - Y_i \log (\sum_{j \in J_i} e^{U_{ij}-U_{i0}}))
\end{split}
$$
&lt;&#x2F;p&gt;
&lt;p&gt;We started with a basic model that accounts only for how much time has passed from the moment of the order to the proposal from the professional. And this basic model helped us significantly increase the number of deals.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;combined-target&quot; class=&quot;h2&quot;&gt;Combined target&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#combined-target&quot; aria-label=&quot;Anchor link for: combined-target&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s review the components of the successful solution:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A model $f(X_1)$ that takes a feature set $X_1$ and predicts the probability that the professional will send a proposal to the customer.&lt;&#x2F;li&gt;
&lt;li&gt;A model $g(X_2)$ that takes a feature set $X_2$ and predicts the uplift in the probability of a deal in the order caused by a proposal from the professional.&lt;&#x2F;li&gt;
&lt;li&gt;Orders are sorted by the product of these models&#x27; predictions $f(X_1) g(X_2)$.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This solution has its disadvantages:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Using two models in production increases the complexity of the solution.&lt;&#x2F;li&gt;
&lt;li&gt;The model $f(X_1)$ should predict the probability. This limits how the model can be improved. For example, the loss function is the cross-entropy. We cannot use loss functions that are more suitable for the learning to rank problem.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These disadvantages can be resolved by training a model on a combined target. Instead of training a model $f(X_1)$ on an indicator of a proposal $y$, we can train a model $h(X_1, X_2)$ on a combined target $y g(X_2)$. This solution resulted in a further increase in the number of deals.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusions&quot; class=&quot;h2&quot;&gt;Conclusions&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#conclusions&quot; aria-label=&quot;Anchor link for: conclusions&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In two posts I explored the ranking problem in a services marketplace. It&#x27;s a two-sided matching platform where both sides have to choose each other for a deal to happen. I explained the solution that significantly increased the number of deals in the marketplace.&lt;&#x2F;p&gt;
&lt;p&gt;The right problem formulation is more important than a sophisticated solution. When one side searches and proposes, and the other side chooses among the proposals, it&#x27;s not sufficient to maximize the probability of a proposal. Ranking by the uplift in the probability of a deal increased the number of deals in the marketplace.&lt;&#x2F;p&gt;
&lt;p&gt;When two sides choose, it makes sense to model two processes separately:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Probability of a proposal from the side that chooses first.&lt;&#x2F;li&gt;
&lt;li&gt;Uplift in the probability of a match caused by a proposal.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A discrete choice model can be used to estimate the uplift in the probability of a match.&lt;&#x2F;p&gt;
&lt;p&gt;If one of the models should predict the probability measure, it limits the model improvements. Training a single model on a combined target can resolve this issue.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Ranking in a two-sided matching platform (Part 1)</title>
        <published>2023-08-15T00:00:00+00:00</published>
        <updated>2023-08-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Evgeny Ivanov
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://e10v.me/ranking-two-sided-matching-platform-part-1/"/>
        <id>https://e10v.me/ranking-two-sided-matching-platform-part-1/</id>
        
        <content type="html" xml:base="https://e10v.me/ranking-two-sided-matching-platform-part-1/">&lt;h2 id=&quot;overview&quot; class=&quot;h2&quot;&gt;Overview&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#overview&quot; aria-label=&quot;Anchor link for: overview&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Profi is a two-sided matching platform where customers can find tutors, beauty masters, plumbers, and other professionals, and professionals can find customers. It&#x27;s a horizontal marketplace with over 900 types of services provided by professionals.&lt;&#x2F;p&gt;
&lt;p&gt;The quality of search and ranking is important for the matching process there. In a two-sided matching platform, both sides have to choose each other for a deal to happen, and that makes the ranking problem harder and kind of unique.&lt;&#x2F;p&gt;
&lt;p&gt;In two blog posts, I&#x27;ll explore the ranking problem and explain the solution that significantly increased the number of deals in the marketplace.&lt;&#x2F;p&gt;
&lt;p&gt;In this first post, I will cover:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The concept of matching markets and their distinctions from other markets.&lt;&#x2F;li&gt;
&lt;li&gt;The mechanics of Profi&#x27;s matching process.&lt;&#x2F;li&gt;
&lt;li&gt;Business objectives.&lt;&#x2F;li&gt;
&lt;li&gt;The three versions of the problem formulation, with insights into why certain approaches succeeded while others did not.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In the &lt;a href=&quot;https:&#x2F;&#x2F;e10v.me&#x2F;ranking-two-sided-matching-platform-part-2&#x2F;&quot;&gt;second post&lt;&#x2F;a&gt;, I will cover the details of the successful solution that was implemented in production.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;matching-markets&quot; class=&quot;h2&quot;&gt;Matching markets&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#matching-markets&quot; aria-label=&quot;Anchor link for: matching-markets&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;When a customer places an order on Amazon, the seller doesn&#x27;t get to choose whether or not to sell to this customer. And this is an example of a commodity market.&lt;&#x2F;p&gt;
&lt;p&gt;In matching markets, you can&#x27;t just choose and get what you want—you also have to be chosen. Examples of matching markets and platforms:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Job market.&lt;&#x2F;li&gt;
&lt;li&gt;Airbnb before introducing the instant booking feature.&lt;&#x2F;li&gt;
&lt;li&gt;Dating platforms, like Tinder and Bumble.&lt;&#x2F;li&gt;
&lt;li&gt;Platforms for freelancers, like Upwork and Fiverr.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;While in commodity markets prices do most of the work and balance supply and demand, in matching markets, price is not the most important factor. Market design, search, and ranking algorithms play a crucial role there.&lt;&#x2F;p&gt;
&lt;p&gt;To learn more about matching markets read &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.com&#x2F;Who-Gets-What-Why-Matchmaking&#x2F;dp&#x2F;0544705289&#x2F;&quot;&gt;&quot;Who Gets What―and Why: The New Economics of Matchmaking and Market Design&quot;&lt;&#x2F;a&gt; by Alvin Roth, a winner of the Nobel Prize in Economics.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;matching-process&quot; class=&quot;h2&quot;&gt;Matching process&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#matching-process&quot; aria-label=&quot;Anchor link for: matching-process&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The process of how customers and professionals can find each other on Profi:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Customers place orders and describe the details of the work.&lt;&#x2F;li&gt;
&lt;li&gt;Professionals search, choose orders, and send proposals to the customers.&lt;&#x2F;li&gt;
&lt;li&gt;The customers review the proposals and choose the professionals, or don&#x27;t choose anyone.&lt;&#x2F;li&gt;
&lt;li&gt;If a professional sends a proposal to a customer, and the customer chooses the professional, then this is a deal.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;professionals_choose_customers.svg&quot; class=&quot;figure-img img-fluid mb-0&quot; alt=&quot;Professionals choose orders and send proposals&quot; width=&quot;440px&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Professionals choose orders and send proposals&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;customers_choose_professionals.svg&quot; class=&quot;figure-img img-fluid mb-0&quot; alt=&quot;Customers review proposals and choose professionals&quot; width=&quot;440px&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Customers review proposals and choose professionals&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;&lt;h2 id=&quot;business-objective&quot; class=&quot;h2&quot;&gt;Business objective&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#business-objective&quot; aria-label=&quot;Anchor link for: business-objective&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The order feed in the app for professionals is the source of 56% of proposals. Orders in the feed are selected based on a professional&#x27;s preferences and previous activity. Originally, orders were sorted in reverse chronological order.&lt;&#x2F;p&gt;
&lt;p&gt;While order &quot;freshness&quot; is important for professionals, it&#x27;s not the only factor of choice. The hypothesis was that we could improve the matching process by implementing a new order ranking algorithm.&lt;&#x2F;p&gt;
&lt;p&gt;The objective was to increase the number of deals, which is one of the two most important metrics in the company.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;problem-formulation&quot; class=&quot;h2&quot;&gt;Problem formulation&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#problem-formulation&quot; aria-label=&quot;Anchor link for: problem-formulation&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;probability-of-a-proposal&quot; class=&quot;h4&quot;&gt;Probability of a proposal&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#probability-of-a-proposal&quot; aria-label=&quot;Anchor link for: probability-of-a-proposal&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The first idea was to sort orders by the probability of a proposal. The intuition was the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Sorting by the probability of a proposal would help professionals to find more relevant orders.&lt;&#x2F;li&gt;
&lt;li&gt;Professionals would send more proposals.&lt;&#x2F;li&gt;
&lt;li&gt;Customers would receive more proposals and have more choice.&lt;&#x2F;li&gt;
&lt;li&gt;A wider choice of proposals would increase the probability of finding the right professional.&lt;&#x2F;li&gt;
&lt;li&gt;The number of deals would increase.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;probability_of_proposal.svg&quot; class=&quot;figure-img img-fluid mb-0&quot; alt=&quot;Predicting the probability of a proposal&quot; width=&quot;440px&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Predicting the probability of a proposal&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;We trained a machine learning model with the following target and conducted an experiment:&lt;&#x2F;p&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;
  &lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Proposal&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Deal&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Target&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Yes&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Yes&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Yes&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;No&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;No&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;No&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;figcaption class=&quot;figure-caption&quot;&gt;Predicting the probability of a proposal&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;The experiment resulted in the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The number of proposals had indeed increased.&lt;&#x2F;li&gt;
&lt;li&gt;But the number of deals had not increased.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The interpretation was that extra proposals were sent to customers who already had enough proposals to choose from.&lt;&#x2F;p&gt;
&lt;p&gt;Increasing the number of proposals without increasing the number of deals is not good for either professionals or customers:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Professionals make an extra effort to write and send proposals, but they don&#x27;t get more customers.&lt;&#x2F;li&gt;
&lt;li&gt;Customers are distracted by additional proposals while they already have enough to choose from.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;probability-of-a-deal&quot; class=&quot;h4&quot;&gt;Probability of a deal&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#probability-of-a-deal&quot; aria-label=&quot;Anchor link for: probability-of-a-deal&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The next intuitive choice was to sort orders by the probability of a deal, and to train a model with the following target:&lt;&#x2F;p&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;
  &lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Proposal&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Deal&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Target&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Yes&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Yes&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Yes&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;No&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;No&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;No&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;figcaption class=&quot;figure-caption&quot;&gt;Predicting the probability of a deal&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;To be precise, for a pair of a professional and a customer &lt;em&gt;(P, C)&lt;&#x2F;em&gt;, a model should have predicted the probability that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The professional &lt;em&gt;P&lt;&#x2F;em&gt; would send a proposal to the customer &lt;em&gt;C&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;And the customer &lt;em&gt;C&lt;&#x2F;em&gt; would choose the professional &lt;em&gt;P&lt;&#x2F;em&gt; among others.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;probability_of_deal.svg&quot; class=&quot;figure-img img-fluid mb-0&quot; alt=&quot;Predicting the probability of a deal&quot; width=&quot;440px&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Predicting the probability of a deal&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;But, if you think carefully, you can see that this is not the right problem formulation in this case:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;First, imagine that the first professional on the picture above has sent a proposal to the first customer, and that this customer has chosen this professional.&lt;&#x2F;li&gt;
&lt;li&gt;Then the second professional wouldn&#x27;t get a deal with the first customer. Does that mean that the second professional shouldn&#x27;t have sent the proposal?&lt;&#x2F;li&gt;
&lt;li&gt;Now, imagine that the first professional hasn&#x27;t sent a proposal. Then the proposal from the second professional could be valuable.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;customer_can_choose_another_professional.svg&quot; class=&quot;figure-img img-fluid mb-0&quot; alt=&quot;A customer can choose another professional&quot; width=&quot;440px&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;A customer can choose another professional&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;We decided not to move forward with this formulation of the problem as it doesn&#x27;t correctly reflect the matching process.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;uplift-in-the-probability-of-a-deal&quot; class=&quot;h4&quot;&gt;Uplift in the probability of a deal&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#uplift-in-the-probability-of-a-deal&quot; aria-label=&quot;Anchor link for: uplift-in-the-probability-of-a-deal&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The last idea was to estimate how showing an order to a professional would change the probability of a deal in that order, no matter with which professional. The model should calculate a difference in the probability of a deal in an order between two parallel universes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The one where we show the order to the professional.&lt;&#x2F;li&gt;
&lt;li&gt;The one where we don&#x27;t show the order to the professional.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Models that compare parallel universes are called uplift models.&lt;&#x2F;p&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;img src=&quot;uplift_in_probability_of_deal.svg&quot; class=&quot;figure-img img-fluid mb-0&quot; alt=&quot;Uplift in the probability of a deal&quot; width=&quot;440px&quot;&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Uplift in the probability of a deal&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;Ranking orders by the uplift in the probability of a deal showed the following results in the experiment (measured with proxy metrics):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The number of deals coming from the order feed had increased by 10%.&lt;&#x2F;li&gt;
&lt;li&gt;The total number of deals had increased by 5%.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It was a great result in itself. But it also opened a door for further improvements, not only in the ranking of orders. For example, finding more relevant orders and adding them to the order feed automatically increases the number of deals.&lt;&#x2F;p&gt;
&lt;p&gt;I will get into some details of the model in the &lt;a href=&quot;https:&#x2F;&#x2F;e10v.me&#x2F;ranking-two-sided-matching-platform-part-2&#x2F;&quot;&gt;next post&lt;&#x2F;a&gt;. Stay tuned.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;p-s-type-iii-error&quot; class=&quot;h2&quot;&gt;P.S. Type III error&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#p-s-type-iii-error&quot; aria-label=&quot;Anchor link for: p-s-type-iii-error&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This case reminded me of a notion of a type III error. Before I explain what it means, let me give you examples of type I and type II errors in the context of the ranking of orders:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Imagine we show an order to a professional and expect the professional to send a proposal. But the professional doesn&#x27;t send a proposal. That&#x27;s a type I error.&lt;&#x2F;li&gt;
&lt;li&gt;Now imagine we don&#x27;t show an order to a professional because we don&#x27;t expect the professional to send a proposal. But if we showed it, the professional would send a proposal. That&#x27;s a type II error.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Well, whether the professional would send a proposal or not is not the right question in the first place, at least not the final one. And that&#x27;s a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Type_III_error&quot;&gt;type III error&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Also, check out Cassie Kozyrkov&#x27;s excellent explanation: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;R51nueuAyIY&quot;&gt;https:&#x2F;&#x2F;youtu.be&#x2F;R51nueuAyIY&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>rico: rich content to HTML as easy as Doc(df, plot)</title>
        <published>2023-08-10T00:00:00+00:00</published>
        <updated>2023-08-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Evgeny Ivanov
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://e10v.me/rico-rich-content-to-html-easy/"/>
        <id>https://e10v.me/rico-rich-content-to-html-easy/</id>
        
        <content type="html" xml:base="https://e10v.me/rico-rich-content-to-html-easy/">&lt;h2 id=&quot;what-is-rico&quot; class=&quot;h2&quot;&gt;What is rico?&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#what-is-rico&quot; aria-label=&quot;Anchor link for: what-is-rico&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;rico&quot;&gt;rico&lt;&#x2F;a&gt; is a Python package for creating HTML documents from rich content: dataframes, plots, images, markdown, etc. It provides a high-level, easy-to-use API with reasonable defaults, as well as low-level access for better control.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-rico&quot; class=&quot;h2&quot;&gt;Why rico?&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#why-rico&quot; aria-label=&quot;Anchor link for: why-rico&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;One might wonder why they need another package if they can create HTML documents with Jupyter and nbconvert. If you are working with data in Jupyter notebooks, you probably don&#x27;t need rico. But if you are working with data in Python scripts, rico can be helpful.&lt;&#x2F;p&gt;
&lt;p&gt;Here is my use case. I&#x27;m working with data in Python scripts and organizing them into data pipelines using Prefect. Sometimes I want to create a report as the last step of a pipeline. With Jupyter I would have the following routine:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Create a task to save all the dataframes I need for my report in Parquet files or in a database.&lt;&#x2F;li&gt;
&lt;li&gt;Create a Jupyter notebook to load the dataframes and visualize all the tables and plots I need.&lt;&#x2F;li&gt;
&lt;li&gt;Create a task to convert the notebook to HTML using nbconvert.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Creating an extra task and an extra notebook is not what bothers me the most. I have to think about how to pass data to a notebook. Sure, it&#x27;s not a very hard job, but it&#x27;s not a job I want to spend my time on.&lt;&#x2F;p&gt;
&lt;p&gt;With rico, I simply create a task to add all objects I want to visualize to a document and save it to an HTML file.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-use-rico&quot; class=&quot;h2&quot;&gt;How to use rico?&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#how-to-use-rico&quot; aria-label=&quot;Anchor link for: how-to-use-rico&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;installation&quot; class=&quot;h4&quot;&gt;Installation&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#installation&quot; aria-label=&quot;Anchor link for: installation&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;pip&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; install&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; rico&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;rico has no dependencies other than standard Python packages.&lt;&#x2F;p&gt;
&lt;p&gt;For Markdown support install &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;executablebooks&#x2F;markdown-it-py&quot;&gt;markdown-it-py&lt;&#x2F;a&gt; or &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Python-Markdown&#x2F;markdown&quot;&gt;Python Markdown&lt;&#x2F;a&gt; or set your own Markdown renderer using &lt;code&gt;rico.set_config&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;basic-usage&quot; class=&quot;h4&quot;&gt;Basic usage&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#basic-usage&quot; aria-label=&quot;Anchor link for: basic-usage&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;rico provides both declarative and imperative interfaces.&lt;&#x2F;p&gt;
&lt;p&gt;Declarative style:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; pandas&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; pd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; rico&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;df&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; pd&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;DataFrame&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-begin z-python&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 7&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 7&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-begin z-python&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 9&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 7&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-list z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    index&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;pd&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Index&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-support z-type z-python&quot;&gt;list&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;AAABBBCCC&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;plot&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;plot&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;scatter&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello, World!&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; plot&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; title&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;My doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The result:&lt;&#x2F;p&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;a href=&quot;basic_usage.png&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;basic_usage.png&quot; class=&quot;figure-img img-thumbnail&quot; alt=&quot;Basic usage&quot; width=&quot;480px&quot;&gt;&lt;&#x2F;a&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Click on image to zoom in.&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;Imperative style:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;title&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;My doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello, World!&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; plot&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The result will look the same.&lt;&#x2F;p&gt;
&lt;p&gt;Serialize the document to HTML using &lt;code&gt;str(doc)&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;with&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; open&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;doc.html&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; f&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;write&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-type z-support z-type z-python&quot;&gt;str&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Also, you can call &lt;code&gt;doc.serialize(indent=True)&lt;&#x2F;code&gt; to indent the HTML element tree. For brevity, I will omit the serialization and saving to file in the following examples.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;content-types&quot; class=&quot;h4&quot;&gt;Content types&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#content-types&quot; aria-label=&quot;Anchor link for: content-types&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;rico automatically recognizes the following content types:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;rico content classes (subclasses of &lt;code&gt;rico.ContentBase&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;Matplotlib Pyplot Plots.&lt;&#x2F;li&gt;
&lt;li&gt;Dataframes, Seaborn plots, Altair charts and other types with &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ipython.readthedocs.io&#x2F;en&#x2F;stable&#x2F;config&#x2F;integrating.html&quot;&gt;IPython rich representation methods&lt;&#x2F;a&gt;. Basically, rico supports the same types as Jupyter.&lt;&#x2F;li&gt;
&lt;li&gt;Text.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Use specific classes for plots and text to change the default behavior:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Text&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello, World!&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; mono&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The default value is False.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Plot&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;plot&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; format&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;png&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; bbox_inches&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;tight&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; The default value is &amp;quot;svg&amp;quot;.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    title&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;My doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The following code gives the same result as the code above:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;title&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;My doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append_text&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello, World!&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; mono&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append_plot&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;plot&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; format&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;png&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; bbox_inches&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;tight&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Use specific classes and methods for other content types:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Images: &lt;code&gt;Image&lt;&#x2F;code&gt; or &lt;code&gt;Doc.append_image&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Code: &lt;code&gt;Code&lt;&#x2F;code&gt; or &lt;code&gt;Doc.append_code&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Markdown: &lt;code&gt;Markdown&lt;&#x2F;code&gt; or &lt;code&gt;Doc.append_markdown&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;HTML tag: &lt;code&gt;Tag&lt;&#x2F;code&gt; or &lt;code&gt;Doc.append_tag&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Raw HTML: &lt;code&gt;HTML&lt;&#x2F;code&gt; or &lt;code&gt;Doc.append_html&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Markdown&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;## Dataframe&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Plot&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; An alternative way to add a header.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    plot&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;HTML&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;lt;h2&amp;gt;Code&amp;lt;&#x2F;h2&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Another way to add a header.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;print(&amp;#39;Hello, World!&amp;#39;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    title&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;My doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The result:&lt;&#x2F;p&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;a href=&quot;content_types.png&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;content_types.png&quot; class=&quot;figure-img img-thumbnail&quot; alt=&quot;Content types&quot; width=&quot;480px&quot;&gt;&lt;&#x2F;a&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Click on image to zoom in.&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;The following code gives the same result as the code above:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;title&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;My doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append_markdown&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;## Dataframe&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append_tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Plot&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;plot&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append_html&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;lt;h2&amp;gt;Code&amp;lt;&#x2F;h2&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append_code&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;print(&amp;#39;Hello, World!&amp;#39;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;layout&quot; class=&quot;h4&quot;&gt;Layout&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#layout&quot; aria-label=&quot;Anchor link for: layout&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;rico relies on &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;getbootstrap.com&#x2F;&quot;&gt;Bootstrap&lt;&#x2F;a&gt; styles. The resulting documents are responsive and mobile-friendly. Use Bootstrap classes to control document layout. For example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; altair&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; alt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Dataframes&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Div&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Obj&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;A&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;loc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;A&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; :&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; class_&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;col&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Obj&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;B&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;loc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;B&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; :&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; class_&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;col&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Obj&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;C&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;loc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;C&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; :&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; class_&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;col&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;        class_&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;row row-cols-auto&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;    )&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Plots&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Div&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Obj&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;A&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            alt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Chart&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;loc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;A&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; :&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mark_point&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;encode&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;            class_&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;col&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;        )&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Obj&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;B&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            alt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Chart&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;loc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;B&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; :&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mark_point&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;encode&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;            class_&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;col&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;        )&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Obj&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;C&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            alt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Chart&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;loc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;C&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; :&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mark_point&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;encode&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;            class_&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;col&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;        )&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;        class_&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;row row-cols-auto&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;    )&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;    title&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Grid system&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The result:&lt;&#x2F;p&gt;
&lt;figure class=&quot;figure text-center mx-auto d-block mb-4&quot;&gt;&lt;a href=&quot;layout.png&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;layout.png&quot; class=&quot;figure-img img-thumbnail&quot; alt=&quot;Layout&quot;&gt;&lt;&#x2F;a&gt;&lt;figcaption class=&quot;figure-caption&quot;&gt;&lt;p&gt;Click on image to zoom in.&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;&lt;&#x2F;figure&gt;
&lt;p&gt;How it works:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rico.Div&lt;&#x2F;code&gt; represents an HTML container (&lt;code&gt;&amp;lt;div&amp;gt;&lt;&#x2F;code&gt;). You can add objects to it, just like with &lt;code&gt;rico.Doc&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Each content element is also wrapped in a &lt;code&gt;&amp;lt;div&amp;gt;&lt;&#x2F;code&gt; container.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;rico.Obj&lt;&#x2F;code&gt; is a magic class that automatically determines the content type, just like &lt;code&gt;rico.Doc&lt;&#x2F;code&gt; does.&lt;&#x2F;li&gt;
&lt;li&gt;You can specify an HTML &lt;code&gt;class&lt;&#x2F;code&gt; attribute of a &lt;code&gt;&amp;lt;div&amp;gt;&lt;&#x2F;code&gt; container for &lt;code&gt;rico.Div&lt;&#x2F;code&gt; and for any content element such as &lt;code&gt;rico.Obj&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;&quot;row&quot;&lt;&#x2F;code&gt;, &lt;code&gt;&quot;row-cols-auto&quot;&lt;&#x2F;code&gt;, &lt;code&gt;&quot;col&quot;&lt;&#x2F;code&gt; classes are part of the Bootstrap Grid System. Read more in the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;getbootstrap.com&#x2F;docs&#x2F;5.3&#x2F;layout&#x2F;grid&#x2F;&quot;&gt;documentation&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The following code gives the same result as the code above:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;title&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Grid system&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append_tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Dataframes&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;div1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Div&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;class_&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;row row-cols-auto&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;div1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; data&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;groupby&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;index&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    div1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; class_&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;col&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append_tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Plots&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;div2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Div&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;class_&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;row row-cols-auto&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;doc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;div2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-element z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; data&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-control&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;groupby&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;df&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;index&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    div2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;append&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        rico&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        alt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;Chart&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;mark_point&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-period z-python&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-generic z-python&quot;&gt;encode&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-begin z-python&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt; y&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-variable&quot;&gt;        class_&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-keyword z-operator z-assignment&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;col&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-arguments z-python&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-arguments z-end z-python&quot;&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;more-information&quot; class=&quot;h2&quot;&gt;More information&amp;nbsp;&lt;a class=&quot;anchor-link&quot; href=&quot;#more-information&quot; aria-label=&quot;Anchor link for: more-information&quot;&gt;#&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;To learn more about how to use rico:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Read the more detailed &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;rico#user-guide&quot;&gt;user guide&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Take a look at the self-explanatory &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;rico&#x2F;tree&#x2F;main&#x2F;examples&quot;&gt;examples&lt;&#x2F;a&gt; with resulting HTML documents.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;rico#installation&quot;&gt;Install&lt;&#x2F;a&gt; rico, create your own documents.&lt;&#x2F;li&gt;
&lt;li&gt;Check the docstrings for details.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Feel free to create an &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;e10v&#x2F;rico&#x2F;issues&quot;&gt;issue&lt;&#x2F;a&gt; to report bugs or submit a suggestion.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
