From 84e76d858548c1cbb0ce7b7dcef9d170ae231ab5 Mon Sep 17 00:00:00 2001 From: Chris Lo Date: Tue, 27 Aug 2024 12:37:02 -0700 Subject: [PATCH 1/3] wk4 --- 01-intro-to-computing.Rmd | 14 +-- 02-data-structures.Rmd | 20 ++-- 03-data-wrangling1.Rmd | 6 +- 04-data-wrangling2.Rmd | 205 ++++++++++++++++++++++++++++++++++++++ _bookdown.yml | 1 + images/join.png | Bin 0 -> 46929 bytes 6 files changed, 227 insertions(+), 19 deletions(-) create mode 100644 04-data-wrangling2.Rmd create mode 100644 images/join.png diff --git a/01-intro-to-computing.Rmd b/01-intro-to-computing.Rmd index 53aeb60..536cced 100644 --- a/01-intro-to-computing.Rmd +++ b/01-intro-to-computing.Rmd @@ -195,7 +195,9 @@ Some types, such as ints, are able to use a more efficient algorithm when invoked using the three argument form. ``` -This shows the function takes in three input arguments: `base`, `exp`, and `mod=None`. When an argument has an assigned value of `mod=None`, that means the input argument already has a value, and you don't need to specify anything, unless you want to. +We can also find a similar help document, in a [nicer rendered form online.](https://docs.python.org/3/library/functions.html#pow) We will practice looking at function documentation throughout the course, because that is a fundamental skill to learn more functions on your own. + +The documentation shows the function takes in three input arguments: `base`, `exp`, and `mod=None`. When an argument has an assigned value of `mod=None`, that means the input argument already has a value, and you don't need to specify anything, unless you want to. The following ways are equivalent ways of using the `pow()` function: @@ -219,11 +221,11 @@ And there is an operational equivalent: We will mostly look at functions with input arguments and return types in this course, but not all functions need to have input arguments and output return. Let's look at some examples of functions that don't always have an input or output: -| Function call | What it takes in | What it does | Returns | -|---------------|---------------|----------------------------|---------------| -| `pow(a, b)` | integer `a`, integer `b` | Raises `a` to the `b`th power. | Integer | -| `print(x)` | any data type `x` | Prints out the value of `x` to the console. | None | -| `dir()` | Nothing | Gives a list of all the variables defined in the environment. | List | +| Function call | What it takes in | What it does | Returns | +|----------------|----------------|-------------------------|----------------| +| [`pow(a, b)`](https://docs.python.org/3/library/functions.html#pow) | integer `a`, integer `b` | Raises `a` to the `b`th power. | Integer | +| [`print(x)`](https://docs.python.org/3/library/functions.html#print) | any data type `x` | Prints out the value of `x` to the console. | None | +| [`dir()`](https://docs.python.org/3/library/functions.html#dir) | Nothing | Gives a list of all the variables defined in the environment. | List | ## Tips on writing your first code diff --git a/02-data-structures.Rmd b/02-data-structures.Rmd index ad3e1dc..ea3b2c2 100644 --- a/02-data-structures.Rmd +++ b/02-data-structures.Rmd @@ -105,12 +105,12 @@ Object methods are functions that does something with the object you are using i Here are some more examples of methods with lists: -| Function method | What it takes in | What it does | Returns | -|----------------|----------------|-------------------------------------|------------------| -| `chrNum.count(x)` | list `chrNum`, data type `x` | Counts the number of instances `x` appears as an element of `chrNum`. | Integer | -| `chrNum.append(x)` | list `chrNum`, data type `x` | Appends `x` to the end of the `chrNum`. | None (but `chrNum` is modified!) | -| `chrNum.sort()` | list `chrNum` | Sorts `chrNum` by ascending order. | None (but `chrNum` is modified!) | -| `chrNum.reverse()` | list `chrNum` | Reverses the order of `chrNum`. | None (but `chrNum` is modified!) | +| Function method | What it takes in | What it does | Returns | +|---------------|---------------|---------------------------|---------------| +| [`chrNum.count(x)`](https://docs.python.org/3/tutorial/datastructures.html) | list `chrNum`, data type `x` | Counts the number of instances `x` appears as an element of `chrNum`. | Integer | +| [`chrNum.append(x)`](https://docs.python.org/3/tutorial/datastructures.html) | list `chrNum`, data type `x` | Appends `x` to the end of the `chrNum`. | None (but `chrNum` is modified!) | +| [`chrNum.sort()`](https://docs.python.org/3/tutorial/datastructures.html) | list `chrNum` | Sorts `chrNum` by ascending order. | None (but `chrNum` is modified!) | +| [`chrNum.reverse()`](https://docs.python.org/3/tutorial/datastructures.html) | list `chrNum` | Reverses the order of `chrNum`. | None (but `chrNum` is modified!) | ## Dataframes @@ -118,7 +118,7 @@ A Dataframe is a two-dimensional data structure that stores data like a spreadsh The Dataframe data structure is found within a Python module called "Pandas". A Python module is an organized collection of functions and data structures. The `import` statement below gives us permission to access the "Pandas" module via the variable `pd`. -To load in a Dataframe from existing spreadsheet data, we use the function `pd.read_csv()`: +To load in a Dataframe from existing spreadsheet data, we use the function [`pd.read_csv()`](https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html): ```{python} import pandas as pd @@ -127,7 +127,7 @@ metadata = pd.read_csv("classroom_data/metadata.csv") type(metadata) ``` -There is a similar function `pd.read_excel()` for loading in Excel spreadsheets. +There is a similar function [`pd.read_excel()`](https://pandas.pydata.org/docs/reference/api/pandas.read_excel.html) for loading in Excel spreadsheets. Let's investigate the Dataframe as an object: @@ -166,7 +166,7 @@ metadata.shape ### What can a Dataframe do (in terms of operations and functions)? -We can use the `head()` and `tail()` functions to look at the first few rows and last few rows of `metadata`, respectively: +We can use the [`.head()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.head.html) and [`.tail()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.tail.html) methods to look at the first few rows and last few rows of `metadata`, respectively: ```{python} metadata.head() @@ -179,7 +179,7 @@ Both of these functions (without input arguments) are considered as **methods**: Perhaps the most important operation you will can do with Dataframes is subsetting them. There are two ways to do it. The first way is to subset by numerical indicies, exactly like how we did for lists. -You will use the `iloc` and bracket operations, and you give two slices: one for the row, and one for the column. +You will use the [`iloc`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.iloc.html) and bracket operations, and you give two slices: one for the row, and one for the column. Let's start with a small dataframe to see how it works before returning to `metadata`: diff --git a/03-data-wrangling1.Rmd b/03-data-wrangling1.Rmd index f15538d..9b8cb96 100644 --- a/03-data-wrangling1.Rmd +++ b/03-data-wrangling1.Rmd @@ -65,7 +65,7 @@ expression.head() ``` | Dataframe | The observation is | Some variables are | Some values are | -|------------------|------------------|-------------------|------------------| +|-----------------|-----------------|--------------------|------------------| | metadata | Cell line | ModelID, Age, OncotreeLineage | "ACH-000001", 60, "Myeloid" | | expression | Cell line | KRAS_Exp | 2.4, .3 | | mutation | Cell line | KRAS_Mut | TRUE, FALSE | @@ -127,7 +127,7 @@ Now that your Dataframe has be transformed based on your scientific question, yo If we look at the data structure of a Dataframe's column, it is actually not a List, but an object called Series. It has methods can compute summary statistics for us. Let's take a look at a few popular examples: | Function method | What it takes in | What it does | Returns | -|----------------|----------------|-------------------------|----------------| +|----------------|----------------|------------------------|----------------| | `metadata.Age.mean()` | `metadata.Age` as a numeric Series | Computes the mean value of the `Age` column. | Float (NumPy) | | `metadata['Age'].median()` | `metadata['Age']` as a numeric Series | Computes the median value of the `Age` column. | Float (NumPy) | | `metadata.Age.max()` | `metadata.Age` as a numeric Series | Computes the max value of the `Age` column. | Float (NumPy) | @@ -147,7 +147,7 @@ Notice that the output of some of these methods are Float (NumPy). This refers t We will dedicate extensive time later this course to talk about data visualization, but the Dataframe's column, Series, has a method called `.plot()` that can help us make simple plots for one variable. The `.plot()` method will by default make a line plot, but it is not necessary the plot style we want, so we can give the optional argument `kind` a String value to specify the plot style. We use it for making a histogram or bar plot. | Plot style | Useful for | kind = | Code | -|-----------|-----------|-----------|--------------------------------------| +|-------------|-------------|-------------|---------------------------------| | Histogram | Numerics | "hist" | `metadata.Age.plot(kind = "hist")` | | Bar plot | Strings | "bar" | `metadata.OncotreeSubtype.value_counts().plot(kind = "bar")` | diff --git a/04-data-wrangling2.Rmd b/04-data-wrangling2.Rmd new file mode 100644 index 0000000..8dbfd8d --- /dev/null +++ b/04-data-wrangling2.Rmd @@ -0,0 +1,205 @@ +```{r, include = FALSE} +ottrpal::set_knitr_image_path() +``` + +# Data Wrangling, Part 2 + +We will continue to learn about data analysis with Dataframes. Let's load our three Dataframes from the Depmap project in again: + +```{python} +import pandas as pd +import numpy as np + +metadata = pd.read_csv("classroom_data/metadata.csv") +mutation = pd.read_csv("classroom_data/mutation.csv") +expression = pd.read_csv("classroom_data/expression.csv") +``` + +## Creating new columns + +Often, we want to perform some kind of transformation on our data's columns: perhaps you want to add the values of columns together, or perhaps you want to represent your column in a different scale. + +To create a new column, you simply modify it as if it exists using the bracket operation `[ ]`, and the column will be created: + +```{python} +metadata['AgePlusTen'] = metadata['Age'] + 10 +expression['KRAS_NRAS_exp'] = expression['KRAS_Exp'] + expression['NRAS_Exp'] +expression['log_PIK3CA_Exp'] = np.log(expression['PIK3CA_Exp']) +``` + +where `np.log(x)` is a [function imported from the module NumPy](https://numpy.org/doc/stable/reference/generated/numpy.log.html) that takes in a numeric and returns the log-transformed value. + +Note: you cannot create a new column referring to the attribute of the Dataframe, such as: `expression.KRAS_Exp_log = np.log(expression.KRAS_Exp)`. + +## Merging two Dataframes together + +Suppose we have the following Dataframes: + +`expression` + +| ModelID | PIK3CA_Exp | log_PIK3CA_Exp | +|--------------|------------|----------------| +| "ACH-001113" | 5.138733 | 1.636806 | +| "ACH-001289" | 3.184280 | 1.158226 | +| "ACH-001339" | 3.165108 | 1.152187 | + +`metadata` + +| ModelID | OncotreeLineage | Age | +|--------------|-----------------|-----| +| "ACH-001113" | "Lung" | 69 | +| "ACH-001289" | "CNS/Brain" | NaN | +| "ACH-001339" | "Skin" | 14 | + +Suppose that I want to compare the relationship between `OncotreeLineage` and `PIK3CA_Exp`, but they are columns in different Dataframes. We want a new Dataframe that looks like this: + +| ModelID | PIK3CA_Exp | log_PIK3CA_Exp | OncotreeLineage | Age | +|--------------|------------|----------------|-----------------|-----| +| "ACH-001113" | 5.138733 | 1.636806 | "Lung" | 69 | +| "ACH-001289" | 3.184280 | 1.158226 | "CNS/Brain" | NaN | +| "ACH-001339" | 3.165108 | 1.152187 | "Skin" | 14 | + +We see that in both dataframes, + +- the rows (observations) represent cell lines. + +- there is a common column `ModelID`, with shared values between the two dataframes that can faciltate the merging process. We call this an **index**. + +We will use the method [`.merge()`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.merge.html) for Dataframes. It takes a Dataframe to merge with as the required input argument. The method looks for a common index column between the two dataframes and merge based on that index. + +```{python} +merged = metadata.merge(expression) +``` + +It's usually better to specify what that index column to avoid ambiguity, using the `on` optional argument: + +```{python} +merged = metadata.merge(expression, on='ModelID') +``` + +If the index column for the two Dataframes are named differently, you can specify the column name for each Dataframe: + +```{python} +merged = metadata.merge(expression, left_on='ModelID', right_on='ModelID') +``` + +One of the most import checks you should do when merging dataframes is to look at the number of rows and columns before and after merging to see whether it makes sense or not: + +The number of rows and columns of `metadata`: + +```{python} +metadata.shape +``` + +The number of rows and columns of `expression`: + +```{python} +expression.shape +``` + +The number of rows and columns of `merged`: + +```{python} +merged.shape +``` + +We see that the number of *columns* in `merged` combines the number of columns in `metadata` and `expression`, while the number of *rows* in `merged` is the smaller of the number of rows in `metadata` and `expression`: it only keeps rows that are found in both Dataframe's index columns. This kind of join is called "inner join", because in the Venn Diagram of elements common in both index column, we keep the inner overlap: + +![](images/join.png) + +You can specifiy the join style by changing the optional input argument `how`. + +- `how = "outer"` keeps all observations. + +- `how = "left"` keeps all observations in the left Dataframe. + +- `how = "right"` keeps all observations in the right Dataframe. + +- `how = "inner"` keeps observations common to both Dataframe. This is the default value of `how`. + +## Grouping and summarizing Dataframes + +In a dataset, there may be groups of observations that we want to understand, such as case vs. control, or comparing different cancer subtypes. For example, in `metadata`, the observation is cell lines, and perhaps we want to group cell lines into their respective cancer type, `OncotreeLineage`, and look at the mean age for each cancer type. + +We want to take `metadata`: + +| ModelID | OncotreeLineage | Age | +|--------------|-----------------|-----| +| "ACH-001113" | "Lung" | 69 | +| "ACH-001289" | "Lung" | 23 | +| "ACH-001339" | "Skin" | 14 | +| "ACH-002342" | "Brain" | 23 | +| "ACH-004854" | "Brain" | 56 | +| "ACH-002921" | "Brain" | 67 | + +into: + +| OncotreeLineage | MeanAge | +|-----------------|---------| +| "Lung" | 46 | +| "Skin" | 14 | +| "Brain" | 48.67 | + +To get there, we need to: + +- **Group** the data based on some criteria, elements of `OncotreeLineage` + +- **Summarize** each group via a summary statistic performed on a column, such as `Age`. + +We first subset the the two columns we need, and then use the methods `group_by(x)` and `.mean()`. + +```{python} +metadata_grouped = metadata.groupby("OncotreeLineage") +metadata_grouped['Age'].mean() +``` + +Here's what's going on: + +- We use the Dataframe method `.group_by(x)` and specify the column we want to group by. The output of this method is a Grouped Dataframe object. It still contains all the information of the `metadata` Dataframe, but it makes a note that it's been grouped. + +- We subset to the column `Age`. The grouping information still persists (This is a Grouped Series object). + +- We use the method `.mean()` to calculate the mean value of `Age` within each group defined by `OncotreeLineage`. + +Alternatively, this could have been done in a chain of methods: + +```{python} +metadata.groupby("OncotreeLineage")["Age"].mean() +``` + +Once a Dataframe has been grouped and a column is selected, all the summary statistics methods you learned from last week, such as `.mean()`, `.median()`, `.max()`, can be used. One new summary statistics method that is useful for this grouping and summarizing analysis is `.count()` which tells you how many entries are counted within each group. + +### Optional: Multiple grouping, Multiple columns, Multiple summary statistics + +Sometimes, when performing grouping and summary analysis, you want to operate on multiple columns simultaneously. + +For example, you may want to group by a combination of `OncotreeLineage` and `AgeCategory`, such as "Lung" and "Adult" as one grouping. You can do so like this: + +```{python} +metadata_grouped = metadata.groupby(["OncotreeLineage", "AgeCategory"]) +metadata_grouped['Age'].mean() +``` + +You can also summarize on multiple columns simultaneously. For each column, you have to specify what summary statistic functions you want to use. This can be specified via the `.agg(x)` method on a Grouped Dataframe. + +For example, coming back to our age case-control Dataframe, + +```{python} +df = pd.DataFrame(data={'status': ["treated", "untreated", "untreated", "discharged", "treated"], + 'age_case': [25, 43, 21, 65, 7], + 'age_control': [49, 20, 32, 25, 32]}) + +df +``` + +We group by `status` and summarize `age_case` and `age_control` with a few summary statistics each: + +```{python} +df.groupby("status").agg({"age_case": "mean", "age_control": ["min", "max", "mean"]}) +``` + +The input argument to the `.agg(x)` method is called a Dictionary, which let's you structure information in a paired relationship. You can learn more about dictionaries [here](https://docs.python.org/3/tutorial/datastructures.html#dictionaries). + +## Exercises + +Exercise for week 4 can be found [here](). diff --git a/_bookdown.yml b/_bookdown.yml index 90c6f3a..31cc82e 100644 --- a/_bookdown.yml +++ b/_bookdown.yml @@ -5,6 +5,7 @@ rmd_files: ["index.Rmd", "01-intro-to-computing.Rmd", "02-data-structures.Rmd", "03-data-wrangling1.Rmd", + "04-data-wrangling2.Rmd", "About.Rmd", "References.Rmd"] new_session: yes diff --git a/images/join.png b/images/join.png new file mode 100644 index 0000000000000000000000000000000000000000..d408d6b89fa9aaeebfe3518c056c43ae8bac0da0 GIT binary patch literal 46929 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 K$<|1lhx|W$7Ic3A literal 0 HcmV?d00001 From 13194c7da1317258a439bd993bdeeae546785ecf Mon Sep 17 00:00:00 2001 From: Chris Lo Date: Tue, 27 Aug 2024 12:37:34 -0700 Subject: [PATCH 2/3] url --- 04-data-wrangling2.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/04-data-wrangling2.Rmd b/04-data-wrangling2.Rmd index 8dbfd8d..17a3258 100644 --- a/04-data-wrangling2.Rmd +++ b/04-data-wrangling2.Rmd @@ -202,4 +202,4 @@ The input argument to the `.agg(x)` method is called a Dictionary, which let's y ## Exercises -Exercise for week 4 can be found [here](). +Exercise for week 4 can be found [here](https://colab.research.google.com/drive/1ntkUdKQ209vu1M89rcsBst-pKKuwzdwX?usp=sharing). From 8b6d4559887e9c4706ddc8ae15558586adab3b26 Mon Sep 17 00:00:00 2001 From: Chris Lo Date: Tue, 27 Aug 2024 13:08:10 -0700 Subject: [PATCH 3/3] add doc URLs --- 03-data-wrangling1.Rmd | 14 +++++++------- 04-data-wrangling2.Rmd | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/03-data-wrangling1.Rmd b/03-data-wrangling1.Rmd index 9b8cb96..eeac127 100644 --- a/03-data-wrangling1.Rmd +++ b/03-data-wrangling1.Rmd @@ -94,7 +94,7 @@ To subset for rows implicitly, we will use the conditional operators on Datafram metadata['OncotreeLineage'] == "Lung" ``` -Then, we will use the `.loc` operation (which is different than `.iloc` operation!) and subsetting brackets to subset rows and columns Age and Sex at the same time: +Then, we will use the [`.loc`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.loc.html) operation (which is different than [`.iloc`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.iloc.html) operation!) and subsetting brackets to subset rows and columns Age and Sex at the same time: ```{python} metadata.loc[metadata['OncotreeLineage'] == "Lung", ["Age", "Sex"]] @@ -126,12 +126,12 @@ Now that your Dataframe has be transformed based on your scientific question, yo If we look at the data structure of a Dataframe's column, it is actually not a List, but an object called Series. It has methods can compute summary statistics for us. Let's take a look at a few popular examples: -| Function method | What it takes in | What it does | Returns | +| Function method | What it takes in | What it does | Returns | |----------------|----------------|------------------------|----------------| -| `metadata.Age.mean()` | `metadata.Age` as a numeric Series | Computes the mean value of the `Age` column. | Float (NumPy) | -| `metadata['Age'].median()` | `metadata['Age']` as a numeric Series | Computes the median value of the `Age` column. | Float (NumPy) | -| `metadata.Age.max()` | `metadata.Age` as a numeric Series | Computes the max value of the `Age` column. | Float (NumPy) | -| `metadata.OncotreeSubtype.value_counts()` | `metadata.OncotreeSubtype` as a string Series | Creates a frequency table of all unique elements in `OncotreeSubtype` column. | Series | +| [`metadata.Age.mean()`](https://pandas.pydata.org/docs/reference/api/pandas.Series.mean.html) | `metadata.Age` as a numeric Series | Computes the mean value of the `Age` column. | Float (NumPy) | +| [`metadata['Age'].median()`](https://pandas.pydata.org/docs/reference/api/pandas.Series.median.html) | `metadata['Age']` as a numeric Series | Computes the median value of the `Age` column. | Float (NumPy) | +| [`metadata.Age.max()`](https://pandas.pydata.org/docs/reference/api/pandas.Series.max.html) | `metadata.Age` as a numeric Series | Computes the max value of the `Age` column. | Float (NumPy) | +| [`metadata.OncotreeSubtype.value_counts()`](https://pandas.pydata.org/docs/reference/api/pandas.Series.value_counts.html) | `metadata.OncotreeSubtype` as a string Series | Creates a frequency table of all unique elements in `OncotreeSubtype` column. | Series | Let's try it out, with some nice print formatting: @@ -144,7 +144,7 @@ Notice that the output of some of these methods are Float (NumPy). This refers t ## Simple data visualization -We will dedicate extensive time later this course to talk about data visualization, but the Dataframe's column, Series, has a method called `.plot()` that can help us make simple plots for one variable. The `.plot()` method will by default make a line plot, but it is not necessary the plot style we want, so we can give the optional argument `kind` a String value to specify the plot style. We use it for making a histogram or bar plot. +We will dedicate extensive time later this course to talk about data visualization, but the Dataframe's column, Series, has a method called [`.plot()`](https://pandas.pydata.org/docs/reference/api/pandas.Series.plot.html) that can help us make simple plots for one variable. The `.plot()` method will by default make a line plot, but it is not necessary the plot style we want, so we can give the optional argument `kind` a String value to specify the plot style. We use it for making a histogram or bar plot. | Plot style | Useful for | kind = | Code | |-------------|-------------|-------------|---------------------------------| diff --git a/04-data-wrangling2.Rmd b/04-data-wrangling2.Rmd index 17a3258..3e51640 100644 --- a/04-data-wrangling2.Rmd +++ b/04-data-wrangling2.Rmd @@ -27,7 +27,7 @@ expression['KRAS_NRAS_exp'] = expression['KRAS_Exp'] + expression['NRAS_Exp'] expression['log_PIK3CA_Exp'] = np.log(expression['PIK3CA_Exp']) ``` -where `np.log(x)` is a [function imported from the module NumPy](https://numpy.org/doc/stable/reference/generated/numpy.log.html) that takes in a numeric and returns the log-transformed value. +where [`np.log(x)`](https://numpy.org/doc/stable/reference/generated/numpy.log.html) is a function imported from the module NumPy that takes in a numeric and returns the log-transformed value. Note: you cannot create a new column referring to the attribute of the Dataframe, such as: `expression.KRAS_Exp_log = np.log(expression.KRAS_Exp)`. @@ -109,7 +109,7 @@ We see that the number of *columns* in `merged` combines the number of columns i You can specifiy the join style by changing the optional input argument `how`. -- `how = "outer"` keeps all observations. +- `how = "outer"` keeps all observations - also known as a "full join" - `how = "left"` keeps all observations in the left Dataframe. @@ -146,7 +146,7 @@ To get there, we need to: - **Summarize** each group via a summary statistic performed on a column, such as `Age`. -We first subset the the two columns we need, and then use the methods `group_by(x)` and `.mean()`. +We first subset the the two columns we need, and then use the methods [`.group_by(x)`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.groupby.html) and `.mean()`. ```{python} metadata_grouped = metadata.groupby("OncotreeLineage") @@ -155,7 +155,7 @@ metadata_grouped['Age'].mean() Here's what's going on: -- We use the Dataframe method `.group_by(x)` and specify the column we want to group by. The output of this method is a Grouped Dataframe object. It still contains all the information of the `metadata` Dataframe, but it makes a note that it's been grouped. +- We use the Dataframe method [`.group_by(x)`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.groupby.html) and specify the column we want to group by. The output of this method is a Grouped Dataframe object. It still contains all the information of the `metadata` Dataframe, but it makes a note that it's been grouped. - We subset to the column `Age`. The grouping information still persists (This is a Grouped Series object). @@ -167,7 +167,7 @@ Alternatively, this could have been done in a chain of methods: metadata.groupby("OncotreeLineage")["Age"].mean() ``` -Once a Dataframe has been grouped and a column is selected, all the summary statistics methods you learned from last week, such as `.mean()`, `.median()`, `.max()`, can be used. One new summary statistics method that is useful for this grouping and summarizing analysis is `.count()` which tells you how many entries are counted within each group. +Once a Dataframe has been grouped and a column is selected, all the summary statistics methods you learned from last week, such as `.mean()`, `.median()`, `.max()`, can be used. One new summary statistics method that is useful for this grouping and summarizing analysis is [`.count()`](https://pandas.pydata.org/docs/reference/api/pandas.Series.count.html) which tells you how many entries are counted within each group. ### Optional: Multiple grouping, Multiple columns, Multiple summary statistics @@ -180,7 +180,7 @@ metadata_grouped = metadata.groupby(["OncotreeLineage", "AgeCategory"]) metadata_grouped['Age'].mean() ``` -You can also summarize on multiple columns simultaneously. For each column, you have to specify what summary statistic functions you want to use. This can be specified via the `.agg(x)` method on a Grouped Dataframe. +You can also summarize on multiple columns simultaneously. For each column, you have to specify what summary statistic functions you want to use. This can be specified via the [`.agg(x)`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.agg.html) method on a Grouped Dataframe. For example, coming back to our age case-control Dataframe, @@ -198,7 +198,7 @@ We group by `status` and summarize `age_case` and `age_control` with a few summa df.groupby("status").agg({"age_case": "mean", "age_control": ["min", "max", "mean"]}) ``` -The input argument to the `.agg(x)` method is called a Dictionary, which let's you structure information in a paired relationship. You can learn more about dictionaries [here](https://docs.python.org/3/tutorial/datastructures.html#dictionaries). +The input argument to the `.agg(x)` method is called a [Dictionary](https://docs.python.org/3/tutorial/datastructures.html#dictionaries), which let's you structure information in a paired relationship. You can learn more about dictionaries here. ## Exercises