{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Intermediate Pandas\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Today we look at some ways to use Pandas DataFrames like databases.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Revisiting a previous example with batches of data\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We start with the example we looked at before. It is a dataset from a set of experiments. The experiments are grouped by the Day they were run on. We will use Pandas to do some analysis by the day.\n", "\n" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Run orderDayAmbient TemperatureTemperaturePressureFitted ValueResidual
01123.82054.749225.066222.9202.146
12124.12023.323100.33199.4110.920
23123.43458.775230.863238.744-7.881
34123.99325.854106.160109.359-3.199
45123.37568.297277.502276.1651.336
56123.23337.481148.314155.056-6.741
67124.16249.542197.562202.456-4.895
78123.66734.101138.537141.770-3.232
89124.05633.901137.969140.983-3.014
910122.78629.242117.410122.674-5.263
1011223.78539.506164.442163.0131.429
1112222.98743.004181.044176.7594.285
1213223.79953.226222.179216.9335.246
1314223.66154.467227.010221.8135.198
1415223.85257.549232.496233.925-1.429
1516223.37961.204253.557248.2885.269
1617224.14631.489139.894131.5068.388
1718224.18768.476273.931276.871-2.940
1819224.15951.144207.969208.753-0.784
1920223.80368.774280.205278.0402.165
2021324.38155.350227.060225.2821.779
2122324.02744.692180.605183.396-2.791
2223324.34250.995206.229208.167-1.938
2324323.67021.60291.46492.649-1.186
2425324.24654.673223.869222.6221.247
2526325.08241.449172.910170.6512.259
2627324.57535.451152.073147.0754.998
2728323.80342.989169.427176.703-7.276
2829324.66048.599192.561198.748-6.188
2930324.09721.44894.44892.0422.406
3031422.81656.982222.794231.697-8.902
3132424.16747.901199.003196.0082.996
3233422.71240.285168.668166.0772.592
3334423.61125.609109.387108.3970.990
3435423.35422.97198.44598.0290.416
3536423.66925.838110.987109.2951.692
3637423.96549.127202.662200.8261.835
3738422.91754.936224.773223.6531.120
3839423.54650.917216.058207.8598.199
3940424.45041.976171.469172.720-1.251
\n", "
" ], "text/plain": [ " Run order Day Ambient Temperature Temperature Pressure Fitted Value \\\n", "0 1 1 23.820 54.749 225.066 222.920 \n", "1 2 1 24.120 23.323 100.331 99.411 \n", "2 3 1 23.434 58.775 230.863 238.744 \n", "3 4 1 23.993 25.854 106.160 109.359 \n", "4 5 1 23.375 68.297 277.502 276.165 \n", "5 6 1 23.233 37.481 148.314 155.056 \n", "6 7 1 24.162 49.542 197.562 202.456 \n", "7 8 1 23.667 34.101 138.537 141.770 \n", "8 9 1 24.056 33.901 137.969 140.983 \n", "9 10 1 22.786 29.242 117.410 122.674 \n", "10 11 2 23.785 39.506 164.442 163.013 \n", "11 12 2 22.987 43.004 181.044 176.759 \n", "12 13 2 23.799 53.226 222.179 216.933 \n", "13 14 2 23.661 54.467 227.010 221.813 \n", "14 15 2 23.852 57.549 232.496 233.925 \n", "15 16 2 23.379 61.204 253.557 248.288 \n", "16 17 2 24.146 31.489 139.894 131.506 \n", "17 18 2 24.187 68.476 273.931 276.871 \n", "18 19 2 24.159 51.144 207.969 208.753 \n", "19 20 2 23.803 68.774 280.205 278.040 \n", "20 21 3 24.381 55.350 227.060 225.282 \n", "21 22 3 24.027 44.692 180.605 183.396 \n", "22 23 3 24.342 50.995 206.229 208.167 \n", "23 24 3 23.670 21.602 91.464 92.649 \n", "24 25 3 24.246 54.673 223.869 222.622 \n", "25 26 3 25.082 41.449 172.910 170.651 \n", "26 27 3 24.575 35.451 152.073 147.075 \n", "27 28 3 23.803 42.989 169.427 176.703 \n", "28 29 3 24.660 48.599 192.561 198.748 \n", "29 30 3 24.097 21.448 94.448 92.042 \n", "30 31 4 22.816 56.982 222.794 231.697 \n", "31 32 4 24.167 47.901 199.003 196.008 \n", "32 33 4 22.712 40.285 168.668 166.077 \n", "33 34 4 23.611 25.609 109.387 108.397 \n", "34 35 4 23.354 22.971 98.445 98.029 \n", "35 36 4 23.669 25.838 110.987 109.295 \n", "36 37 4 23.965 49.127 202.662 200.826 \n", "37 38 4 22.917 54.936 224.773 223.653 \n", "38 39 4 23.546 50.917 216.058 207.859 \n", "39 40 4 24.450 41.976 171.469 172.720 \n", "\n", " Residual \n", "0 2.146 \n", "1 0.920 \n", "2 -7.881 \n", "3 -3.199 \n", "4 1.336 \n", "5 -6.741 \n", "6 -4.895 \n", "7 -3.232 \n", "8 -3.014 \n", "9 -5.263 \n", "10 1.429 \n", "11 4.285 \n", "12 5.246 \n", "13 5.198 \n", "14 -1.429 \n", "15 5.269 \n", "16 8.388 \n", "17 -2.940 \n", "18 -0.784 \n", "19 2.165 \n", "20 1.779 \n", "21 -2.791 \n", "22 -1.938 \n", "23 -1.186 \n", "24 1.247 \n", "25 2.259 \n", "26 4.998 \n", "27 -7.276 \n", "28 -6.188 \n", "29 2.406 \n", "30 -8.902 \n", "31 2.996 \n", "32 2.592 \n", "33 0.990 \n", "34 0.416 \n", "35 1.692 \n", "36 1.835 \n", "37 1.120 \n", "38 8.199 \n", "39 -1.251 " ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "\n", "df = pd.read_csv(\n", " \"p-t.dat\",\n", " delimiter=\"\\s+\",\n", " skiprows=2,\n", " names=[\n", " \"Run order\",\n", " \"Day\",\n", " \"Ambient Temperature\",\n", " \"Temperature\",\n", " \"Pressure\",\n", " \"Fitted Value\",\n", " \"Residual\",\n", " ],\n", ")\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first aggregation we will look at is how to make groups of data that are related by values in a column. We use the `groupby` function ([https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.groupby.html#pandas.DataFrame.groupby](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.groupby.html#pandas.DataFrame.groupby)), and specify a column to group on. The result is a `DataFrameGroupBy` object, which we next have to work with.\n", "\n" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "pandas.core.groupby.generic.DataFrameGroupBy" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "groups = df.groupby(\"Day\")\n", "type(groups)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The groups can describe themselves. Here we see we get 4 groups, one for each day, and you can see some statistics about each group. We do not need those for now.\n", "\n" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Run orderAmbient Temperature...Fitted ValueResidual
countmeanstdmin25%50%75%maxcountmean...75%maxcountmeanstdmin25%50%75%max
Day
110.05.53.027651.03.255.57.7510.010.023.6646...217.80400276.16510.0-2.98233.452383-7.881-5.17100-3.2155-0.063502.146
210.015.53.0276511.013.2515.517.7520.010.023.7758...244.69725278.04010.02.68273.606824-2.940-0.230753.22505.234008.388
310.025.53.0276521.023.2525.527.7530.010.024.2883...205.81225225.28210.0-0.66903.948274-7.276-2.577750.03052.139004.998
410.035.53.0276531.033.2535.537.7540.010.023.5207...206.10075231.69710.00.96874.255487-8.9020.559501.40602.402758.199
\n", "

4 rows × 48 columns

\n", "
" ], "text/plain": [ " Run order \\\n", " count mean std min 25% 50% 75% max \n", "Day \n", "1 10.0 5.5 3.02765 1.0 3.25 5.5 7.75 10.0 \n", "2 10.0 15.5 3.02765 11.0 13.25 15.5 17.75 20.0 \n", "3 10.0 25.5 3.02765 21.0 23.25 25.5 27.75 30.0 \n", "4 10.0 35.5 3.02765 31.0 33.25 35.5 37.75 40.0 \n", "\n", " Ambient Temperature ... Fitted Value Residual \\\n", " count mean ... 75% max count mean \n", "Day ... \n", "1 10.0 23.6646 ... 217.80400 276.165 10.0 -2.9823 \n", "2 10.0 23.7758 ... 244.69725 278.040 10.0 2.6827 \n", "3 10.0 24.2883 ... 205.81225 225.282 10.0 -0.6690 \n", "4 10.0 23.5207 ... 206.10075 231.697 10.0 0.9687 \n", "\n", " \n", " std min 25% 50% 75% max \n", "Day \n", "1 3.452383 -7.881 -5.17100 -3.2155 -0.06350 2.146 \n", "2 3.606824 -2.940 -0.23075 3.2250 5.23400 8.388 \n", "3 3.948274 -7.276 -2.57775 0.0305 2.13900 4.998 \n", "4 4.255487 -8.902 0.55950 1.4060 2.40275 8.199 \n", "\n", "[4 rows x 48 columns]" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "groups.describe()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can get a dictionary of the group names and labels from the groups attribute.\n", "\n" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 2: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], 3: [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], 4: [30, 31, 32, 33, 34, 35, 36, 37, 38, 39]}" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "groups.groups" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can get the subset of rows from those group labels.\n", "\n" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Run orderDayAmbient TemperatureTemperaturePressureFitted ValueResidual
1011223.78539.506164.442163.0131.429
1112222.98743.004181.044176.7594.285
1213223.79953.226222.179216.9335.246
1314223.66154.467227.010221.8135.198
1415223.85257.549232.496233.925-1.429
1516223.37961.204253.557248.2885.269
1617224.14631.489139.894131.5068.388
1718224.18768.476273.931276.871-2.940
1819224.15951.144207.969208.753-0.784
1920223.80368.774280.205278.0402.165
\n", "
" ], "text/plain": [ " Run order Day Ambient Temperature Temperature Pressure Fitted Value \\\n", "10 11 2 23.785 39.506 164.442 163.013 \n", "11 12 2 22.987 43.004 181.044 176.759 \n", "12 13 2 23.799 53.226 222.179 216.933 \n", "13 14 2 23.661 54.467 227.010 221.813 \n", "14 15 2 23.852 57.549 232.496 233.925 \n", "15 16 2 23.379 61.204 253.557 248.288 \n", "16 17 2 24.146 31.489 139.894 131.506 \n", "17 18 2 24.187 68.476 273.931 276.871 \n", "18 19 2 24.159 51.144 207.969 208.753 \n", "19 20 2 23.803 68.774 280.205 278.040 \n", "\n", " Residual \n", "10 1.429 \n", "11 4.285 \n", "12 5.246 \n", "13 5.198 \n", "14 -1.429 \n", "15 5.269 \n", "16 8.388 \n", "17 -2.940 \n", "18 -0.784 \n", "19 2.165 " ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.loc[groups.groups[2]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We don't usually work with groups that way though, it is more common to do some analysis on each group.\n", "\n", "Suppose we want to plot the Pressure vs Temperature for each group, so we can see visually if there are any trends that could be attributed to the group. To do this, we need to *iterate* over the groups and then make a plot on each one.\n", "\n", "A `DataFrameGroupBy` is *iterable* and when you loop over it, you get the `key` it was grouped on, and a DataFrame that contains the items in the group. Here we loop over each group, and plot each group with a different color.\n", "\n" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "image/png": "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ZrMZcXFxOHv2LBQKhY9bGNp47/2H995/eO/9i/fffzpz70VRRF1dHWJjYyGRtD8zhz07ACQSCfr37+9WXYVCwX/x/YT33n947/2H996/eP/9x917f6MenVacoExERERBjWGHiIiIghrDjpsiIiKQm5uLiIgIfzcl5PDe+w/vvf/w3vsX77//eOPec4IyERERBTX27BAREVFQY9ghIiKioMawQ0REREGNYYeIiIiCGsPOdfLy8jB06FDI5XKoVCpMmTIFx44dc6gjiiKWLl2K2NhY9OzZE2lpaTh69KifWhw81q5di3vuuce+idSwYcPw2Wef2a/zvvtOXl4eBEFAZmamvYz333uWLl0KQRAcHhqNxn6d9967zp07hxkzZqBv376QyWS49957UVZWZr/O++8dP/nJT5z+vRcEAXPnzgXg+fvOsHOdffv2Ye7cufj6669RVFSElpYWZGRk4PLly/Y6f/jDH7B69WqsWbMGpaWl0Gg0ePjhh+3na1HX9O/fHytXrsShQ4dw6NAhPPTQQ3j00Uft/3LzvvtGaWkp/vrXv+Kee+5xKOf99667774b1dXV9seRI0fs13jvvefixYsYMWIEwsPD8dlnn6GyshKvvvoqevfuba/D++8dpaWlDv/OFxUVAQCeeOIJAF647yK1y2g0igDEffv2iaIoilarVdRoNOLKlSvtdRobG0WlUin+5S9/8Vczg1afPn3Ed955h/fdR+rq6sSBAweKRUVF4qhRo8QFCxaIosh/770tNzdXHDJkiMtrvPfelZ2dLY4cObLd67z/vrNgwQLx9ttvF61Wq1fuO3t2bsBkMgEAoqOjAQBVVVUwGAzIyMiw14mIiMCoUaNw8OBBv7QxGFksFmzevBmXL1/GsGHDeN99ZO7cuZg4cSLS09Mdynn/ve/48eOIjY1FQkICpk2bhu+++w4A77237dixAykpKXjiiSegUqlw33334e2337Zf5/33jatXryI/Px/PPvssBEHwyn1n2GmHKIrIysrCyJEjkZSUBAAwGAwAALVa7VBXrVbbr1HXHTlyBL169UJERAR++ctfYtu2bUhMTOR994HNmzdDr9cjLy/P6Rrvv3fdf//9eO+99/D555/j7bffhsFgwPDhw/HDDz/w3nvZd999h7Vr12LgwIH4/PPP8ctf/hIvvPAC3nvvPQD8d99Xtm/fjkuX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1hEEwV7v7bffxv333+9QTyqVOjxvG2JeffVVvPbaa3j99dcxePBgREVFITMzE1evXm3/iwKQSCQQRdGhrLm52ale28+zWq3QarXYu3evU922c4CI6OYx7BCRR+l0Ohw7dgwDBgzw+HufOXMG58+fR2xsLADgq6++gkQiwR133AG1Wo1bbrkF3333HaZPn96p9/3yyy/x6KOPYsaMGQBsYeT48eO466677HV69OgBi8Xi8LqYmBjU1dXh8uXL9kDTOifnRnQ6HQwGA8LCwvCTn/ykU20los5j2CEij/rtb3+LRx55BHFxcXjiiScgkUhw+PBhHDlyBL///e9v6r0jIyMxc+ZMvPLKKzCbzXjhhRcwdepUaDQaALaVTy+88AIUCgXGjx+PpqYmHDp0CBcvXkRWVla77ztgwAB8/PHHOHjwIPr06YPVq1fDYDA4hJ2f/OQn+Oc//4lTp06hV69eiI6Oxv333w+ZTIYlS5Zg/vz5KCkpwfr16zv8Hunp6Rg2bBimTJmCVatW4c4778T58+exa9cuTJkyxWmYjYhuDldjEZFHjR07Fv/4xz9QVFSEoUOH4oEHHsDq1asRHx9/0+89YMAAPPbYY5gwYQIyMjKQlJTksLT8F7/4Bd555x2sX78egwcPxqhRo7B+/XokJCTc8H1feukl6HQ6jB07FmlpadBoNJgyZYpDnUWLFkEqlSIxMRExMTE4c+YMoqOjkZ+fj127dtmXqy9durTD7yEIAnbt2oUHH3wQzz77LO644w5MmzYNp06dss9fIiLPEcS2A85ERAFo6dKl2L59u1vDRERE12PPDhEREQU1hh0iIiIKahzGIiIioqDGnh0iIiIKagw7REREFNQYdoiIiCioMewQERFRUGPYISIioqDGsENERERBjWGHiIiIghrDDhEREQW1/x/NB4C3WWu8cgAAAABJRU5ErkJggg==\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "fig, ax = plt.subplots()\n", "for day, group in groups:\n", " group.plot(\"Temperature\", \"Pressure\", ax=ax, label=f\"{day}\", style=\"o\")\n", "plt.ylabel(\"Pressure\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "the point of this is we cannot see a visual clustering of the groups by day. That is important, because if we did it could suggest something was different that day.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Combining data sets\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Siddhant Lambor provided from two experiments conducted to measure the properties of a worm-like micelles solution. He had carried out experiments on a rheometer to measure the viscosity of a worm-like micelles solution in a Couette cell geometry and a Cone and Plate geometry. Ideally, there should not be a difference as viscosity is intrinsic to the fluid. Analysis of this data will confirm if that is true. First, we read this data in from the two data files.\n", "\n", "- [cp.xlsx](cp.xlsx) Cone and plate data\n", "- [couette.xlsx](couette.xlsx) Couette data\n", "\n" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StressShear rateViscosityStep timeTemperatureNormal stress
0Pa1/sPa.ss°CPa
10.0099810.049530.20150435.146925.001-0.001263
20.0158170.0798750.19802870.168925.001-0.000835
30.0250710.1273130.196926105.25325-0.000913
40.0397340.2040940.194685140.2625-0.001132
50.0629770.3272530.192442175.31325-0.002026
60.0998060.5303640.188183210.3224.998-0.002429
70.1581650.8754940.180658245.37325-0.001837
80.2506371.491350.168061280.42625.002-0.001655
90.3971312.647070.150027315.43325.001-0.001889
100.6291115.009040.125595350.50125.003-0.002233
110.99615310.39930.09579385.50824.996-0.001807
121.5752824.56240.064134420.60824.998-0.001823
132.4765267.29720.0368455.6325.003-0.00182
143.75092210.9910.017778490.66825-0.002939
\n", "
" ], "text/plain": [ " Stress Shear rate Viscosity Step time Temperature Normal stress\n", "0 Pa 1/s Pa.s s °C Pa\n", "1 0.009981 0.04953 0.201504 35.1469 25.001 -0.001263\n", "2 0.015817 0.079875 0.198028 70.1689 25.001 -0.000835\n", "3 0.025071 0.127313 0.196926 105.253 25 -0.000913\n", "4 0.039734 0.204094 0.194685 140.26 25 -0.001132\n", "5 0.062977 0.327253 0.192442 175.313 25 -0.002026\n", "6 0.099806 0.530364 0.188183 210.32 24.998 -0.002429\n", "7 0.158165 0.875494 0.180658 245.373 25 -0.001837\n", "8 0.250637 1.49135 0.168061 280.426 25.002 -0.001655\n", "9 0.397131 2.64707 0.150027 315.433 25.001 -0.001889\n", "10 0.629111 5.00904 0.125595 350.501 25.003 -0.002233\n", "11 0.996153 10.3993 0.09579 385.508 24.996 -0.001807\n", "12 1.57528 24.5624 0.064134 420.608 24.998 -0.001823\n", "13 2.47652 67.2972 0.0368 455.63 25.003 -0.00182\n", "14 3.75092 210.991 0.017778 490.668 25 -0.002939" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "couette = pd.read_excel(\n", " \"couette.xls\", sheet_name=\"Flow sweep - 1\", header=1\n", ") # sheet name is case sensitive, excel file name is not\n", "\n", "couette" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can drop the row at index 0, it just has the units in it. With this syntax, we have to save the resulting DataFrame back into the variable, or it will not be changed.\n", "\n" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StressShear rateViscosityStep timeTemperatureNormal stress
10.0099810.049530.20150435.146925.001-0.001263
20.0158170.0798750.19802870.168925.001-0.000835
30.0250710.1273130.196926105.25325-0.000913
40.0397340.2040940.194685140.2625-0.001132
50.0629770.3272530.192442175.31325-0.002026
60.0998060.5303640.188183210.3224.998-0.002429
70.1581650.8754940.180658245.37325-0.001837
80.2506371.491350.168061280.42625.002-0.001655
90.3971312.647070.150027315.43325.001-0.001889
100.6291115.009040.125595350.50125.003-0.002233
110.99615310.39930.09579385.50824.996-0.001807
121.5752824.56240.064134420.60824.998-0.001823
132.4765267.29720.0368455.6325.003-0.00182
143.75092210.9910.017778490.66825-0.002939
\n", "
" ], "text/plain": [ " Stress Shear rate Viscosity Step time Temperature Normal stress\n", "1 0.009981 0.04953 0.201504 35.1469 25.001 -0.001263\n", "2 0.015817 0.079875 0.198028 70.1689 25.001 -0.000835\n", "3 0.025071 0.127313 0.196926 105.253 25 -0.000913\n", "4 0.039734 0.204094 0.194685 140.26 25 -0.001132\n", "5 0.062977 0.327253 0.192442 175.313 25 -0.002026\n", "6 0.099806 0.530364 0.188183 210.32 24.998 -0.002429\n", "7 0.158165 0.875494 0.180658 245.373 25 -0.001837\n", "8 0.250637 1.49135 0.168061 280.426 25.002 -0.001655\n", "9 0.397131 2.64707 0.150027 315.433 25.001 -0.001889\n", "10 0.629111 5.00904 0.125595 350.501 25.003 -0.002233\n", "11 0.996153 10.3993 0.09579 385.508 24.996 -0.001807\n", "12 1.57528 24.5624 0.064134 420.608 24.998 -0.001823\n", "13 2.47652 67.2972 0.0368 455.63 25.003 -0.00182\n", "14 3.75092 210.991 0.017778 490.668 25 -0.002939" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "couette = couette.drop(0)\n", "couette" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is a second file called cp.xls we want to combine with this. Here, we combine the drop function all into one line.\n", "\n" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StressShear rateViscosityStep timeTemperatureNormal stress
10.0099840.0531930.18769334.9909251.2658
20.0158220.0820.1929570.090925.0010.569149
30.0250780.1299690.192952105.16250.295899
40.0397590.201760.19706140.213251.0171
50.0629880.3360870.187415175.282250.546196
\n", "
" ], "text/plain": [ " Stress Shear rate Viscosity Step time Temperature Normal stress\n", "1 0.009984 0.053193 0.187693 34.9909 25 1.2658\n", "2 0.015822 0.082 0.19295 70.0909 25.001 0.569149\n", "3 0.025078 0.129969 0.192952 105.16 25 0.295899\n", "4 0.039759 0.20176 0.19706 140.213 25 1.0171\n", "5 0.062988 0.336087 0.187415 175.282 25 0.546196" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "conePlate = pd.read_excel(\"cp.xls\", sheet_name=\"Flow sweep - 1\", header=1).drop(0)\n", "conePlate.head(5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For this analysis, we are only interested in the shear rate, stress and viscosity values. Let us drop the other columns. We do that by the names, and specify inplace=True, which modifies the DataFrame itself.\n", "\n" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StressShear rateViscosity
10.0099840.0531930.187693
20.0158220.0820.19295
30.0250780.1299690.192952
40.0397590.201760.19706
50.0629880.3360870.187415
\n", "
" ], "text/plain": [ " Stress Shear rate Viscosity\n", "1 0.009984 0.053193 0.187693\n", "2 0.015822 0.082 0.19295\n", "3 0.025078 0.129969 0.192952\n", "4 0.039759 0.20176 0.19706\n", "5 0.062988 0.336087 0.187415" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "conePlate.drop([\"Temperature\", \"Step time\", \"Normal stress\"], axis=1, inplace=True)\n", "# if we do not use inplace=True, the data frame will not be changed. It would by default create a new data frame\n", "# and we would have to assign a different variable to capture this change.\n", "conePlate.head(5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We also do that for the couette data. Here we did not use `inplace=True`, so we have to save the result back into the variable to get the change.\n", "\n" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StressShear rateViscosity
10.0099810.049530.201504
20.0158170.0798750.198028
30.0250710.1273130.196926
40.0397340.2040940.194685
50.0629770.3272530.192442
\n", "
" ], "text/plain": [ " Stress Shear rate Viscosity\n", "1 0.009981 0.04953 0.201504\n", "2 0.015817 0.079875 0.198028\n", "3 0.025071 0.127313 0.196926\n", "4 0.039734 0.204094 0.194685\n", "5 0.062977 0.327253 0.192442" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "couette = couette.drop(\n", " [\"Temperature\", \"Step time\", \"Normal stress\"], axis=1\n", ") # without using inplace = True\n", "couette.head(5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see info about each DataFrame like this.\n", "\n" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "RangeIndex: 14 entries, 1 to 14\n", "Data columns (total 3 columns):\n", " # Column Non-Null Count Dtype \n", "--- ------ -------------- ----- \n", " 0 Stress 14 non-null object\n", " 1 Shear rate 14 non-null object\n", " 2 Viscosity 14 non-null object\n", "dtypes: object(3)\n", "memory usage: 468.0+ bytes\n" ] } ], "source": [ "couette.info()" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "RangeIndex: 17 entries, 1 to 17\n", "Data columns (total 3 columns):\n", " # Column Non-Null Count Dtype \n", "--- ------ -------------- ----- \n", " 0 Stress 17 non-null object\n", " 1 Shear rate 17 non-null object\n", " 2 Viscosity 17 non-null object\n", "dtypes: object(3)\n", "memory usage: 540.0+ bytes\n" ] } ], "source": [ "conePlate.info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We could proceed to analyze the DataFrames separately, but instead, we will combine them into one DataFrame. Before doing that, we need to add a column to each one so we know which data set is which. Simply assigning a value to a new column name will do that.\n", "\n" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StressShear rateViscositytype
10.0099810.049530.201504couette
20.0158170.0798750.198028couette
30.0250710.1273130.196926couette
40.0397340.2040940.194685couette
50.0629770.3272530.192442couette
60.0998060.5303640.188183couette
70.1581650.8754940.180658couette
80.2506371.491350.168061couette
90.3971312.647070.150027couette
100.6291115.009040.125595couette
110.99615310.39930.09579couette
121.5752824.56240.064134couette
132.4765267.29720.0368couette
143.75092210.9910.017778couette
\n", "
" ], "text/plain": [ " Stress Shear rate Viscosity type\n", "1 0.009981 0.04953 0.201504 couette\n", "2 0.015817 0.079875 0.198028 couette\n", "3 0.025071 0.127313 0.196926 couette\n", "4 0.039734 0.204094 0.194685 couette\n", "5 0.062977 0.327253 0.192442 couette\n", "6 0.099806 0.530364 0.188183 couette\n", "7 0.158165 0.875494 0.180658 couette\n", "8 0.250637 1.49135 0.168061 couette\n", "9 0.397131 2.64707 0.150027 couette\n", "10 0.629111 5.00904 0.125595 couette\n", "11 0.996153 10.3993 0.09579 couette\n", "12 1.57528 24.5624 0.064134 couette\n", "13 2.47652 67.2972 0.0368 couette\n", "14 3.75092 210.991 0.017778 couette" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "couette[\"type\"] = \"couette\"\n", "couette" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "conePlate[\"type\"] = \"cone\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we can combine these into a single DataFrame. This is not critical, and you can get by without it, but I want to explore the idea, and illustrate it is possible.\n", "\n" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StressShear rateViscositytype
10.0099840.0531930.187693cone
20.0158220.0820.19295cone
30.0250780.1299690.192952cone
40.0397590.201760.19706cone
50.0629880.3360870.187415cone
60.099820.5567490.179291cone
70.158190.9254030.170942cone
80.2507011.575260.159148cone
90.3972632.7820.142798cone
100.6293465.163330.121888cone
110.99686310.40290.095826cone
121.5776823.65580.066693cone
132.4905362.59010.039791cone
143.85502194.2650.019844cone
155.45425795.4740.006857cone
168.621822011.140.004287cone
1713.74983633.470.003784cone
10.0099810.049530.201504couette
20.0158170.0798750.198028couette
30.0250710.1273130.196926couette
40.0397340.2040940.194685couette
50.0629770.3272530.192442couette
60.0998060.5303640.188183couette
70.1581650.8754940.180658couette
80.2506371.491350.168061couette
90.3971312.647070.150027couette
100.6291115.009040.125595couette
110.99615310.39930.09579couette
121.5752824.56240.064134couette
132.4765267.29720.0368couette
143.75092210.9910.017778couette
\n", "
" ], "text/plain": [ " Stress Shear rate Viscosity type\n", "1 0.009984 0.053193 0.187693 cone\n", "2 0.015822 0.082 0.19295 cone\n", "3 0.025078 0.129969 0.192952 cone\n", "4 0.039759 0.20176 0.19706 cone\n", "5 0.062988 0.336087 0.187415 cone\n", "6 0.09982 0.556749 0.179291 cone\n", "7 0.15819 0.925403 0.170942 cone\n", "8 0.250701 1.57526 0.159148 cone\n", "9 0.397263 2.782 0.142798 cone\n", "10 0.629346 5.16333 0.121888 cone\n", "11 0.996863 10.4029 0.095826 cone\n", "12 1.57768 23.6558 0.066693 cone\n", "13 2.49053 62.5901 0.039791 cone\n", "14 3.85502 194.265 0.019844 cone\n", "15 5.45425 795.474 0.006857 cone\n", "16 8.62182 2011.14 0.004287 cone\n", "17 13.7498 3633.47 0.003784 cone\n", "1 0.009981 0.04953 0.201504 couette\n", "2 0.015817 0.079875 0.198028 couette\n", "3 0.025071 0.127313 0.196926 couette\n", "4 0.039734 0.204094 0.194685 couette\n", "5 0.062977 0.327253 0.192442 couette\n", "6 0.099806 0.530364 0.188183 couette\n", "7 0.158165 0.875494 0.180658 couette\n", "8 0.250637 1.49135 0.168061 couette\n", "9 0.397131 2.64707 0.150027 couette\n", "10 0.629111 5.00904 0.125595 couette\n", "11 0.996153 10.3993 0.09579 couette\n", "12 1.57528 24.5624 0.064134 couette\n", "13 2.47652 67.2972 0.0368 couette\n", "14 3.75092 210.991 0.017778 couette" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = pd.concat([conePlate, couette])\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we are ready for the visualization. We will group the DataFrame and then make plots for each group. Here we illustrate several new arguments, including loglog plots, secondary axes, colored tick labels, and multiple legends.\n", "\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "g = df.groupby(\"type\")\n", "ax1 = g.get_group(\"cone\").plot(\n", " \"Shear rate\", \"Viscosity\", logx=True, logy=True, style=\"b.-\", label=\"CP viscosity\"\n", ")\n", "\n", "g.get_group(\"couette\").plot(\n", " \"Shear rate\",\n", " \"Viscosity\",\n", " logx=True,\n", " logy=True,\n", " style=\"g.-\",\n", " ax=ax1,\n", " label=\"Couette viscosity\",\n", ")\n", "\n", "ax2 = g.get_group(\"cone\").plot(\n", " \"Shear rate\",\n", " \"Stress\",\n", " secondary_y=True,\n", " logx=True,\n", " logy=True,\n", " style=\"r.-\",\n", " ax=ax1,\n", " label=\"CP stress\",\n", ")\n", "\n", "g.get_group(\"couette\").plot(\n", " \"Shear rate\",\n", " \"Stress\",\n", " secondary_y=True,\n", " logx=True,\n", " logy=True,\n", " style=\"y.\",\n", " ax=ax2,\n", " label=\"Couette Stress\",\n", ")\n", "\n", "# Setting y axis labels\n", "ax1.set_ylabel(\"Viscosity (Pa.s)\", color=\"b\")\n", "[ticklabel.set_color(\"b\") for ticklabel in ax1.get_yticklabels()]\n", "\n", "ax2.set_ylabel(\"Stress (Pa)\", color=\"r\")\n", "[ticklabel.set_color(\"r\") for ticklabel in ax1.get_yticklabels()]\n", "\n", "# setting legend locations\n", "ax1.legend(loc=6)\n", "ax2.legend(loc=7)\n", "\n", "ax1.set_xlabel(\"Shear rate (1/s)\")\n", "plt.title(\"Comparison of Cone and Plate with Couette Cell\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, in fact we can see these two experiments are practically equivalent.\n", "\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.12" } }, "nbformat": 4, "nbformat_minor": 4 }