{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ccd8d984",
   "metadata": {},
   "source": [
    "# Pandas Practice Questions\n",
    "This notebook contains **20 comprehensive Python pandas practice problems** organized in two sections:\n",
    "\n",
    "**Section A - Short Coding Questions (Questions 1-17):**\n",
    "- Questions 1-12: Basic pandas operations (loading, selection, filtering, handling missing values)\n",
    "- Questions 13-17: Short coding questions on duplicates, missing values, column creation, filtering, and statistics\n",
    "\n",
    "**Section B - Applied Coding Questions (Questions 18-20):**\n",
    "- Question 18: GroupBy with multiple aggregations\n",
    "- Question 19: Advanced filtering and column creation\n",
    "- Question 20: Handling missing values and outliers\n",
    "\n",
    "Each question includes:\n",
    "- Clear problem description\n",
    "- Hints for solving\n",
    "- Multiple-choice code options (where applicable)\n",
    "- Instructor solution with inline examples\n",
    "- Test cases using small DataFrames"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "570dad49",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "from io import StringIO"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "d2a79a76",
   "metadata": {},
   "outputs": [],
   "source": [
    "name = 'student name'\n",
    "roll_number = 'student roll number'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc7b215f",
   "metadata": {},
   "source": [
    "### 1. Load a CSV string into a DataFrame\n",
    "**Return:** A pandas DataFrame from the CSV string\n",
    "\n",
    "**Choose the correct line:**\n",
    "- (a) `return pd.read_excel(StringIO(csv_string))`\n",
    "- (b) `return pd.read_csv(StringIO(csv_string))`\n",
    "- (c) `return pd.DataFrame(csv_string.split('\\n'))`\n",
    "- (d) `return csv_string.to_dataframe()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ef841c30",
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_csv_string(csv_string: str) -> pd.DataFrame:\n",
    "    pass\n",
    "# csv_data = 'name,age,score\\nAlice,25,85\\nBob,30,90\\nCharlie,22,78'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32af94a5",
   "metadata": {},
   "source": [
    "### 2. Get shape and column names\n",
    "**Return:** A tuple of (number of rows, number of columns, list of column names)\n",
    "\n",
    "**Choose the correct code:**\n",
    "- (a) `return (df.size, df.ndim, df.columns)`\n",
    "- (b) `return (df.shape[0], df.shape[1], list(df.columns))`\n",
    "- (c) `return df.info()`\n",
    "- (d) `return (len(df), len(df.index), df.to_list())`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d819f154",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_dataframe_info(df: pd.DataFrame) -> tuple:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c22e3b52",
   "metadata": {},
   "source": [
    "### 3. Get the first n rows of a DataFrame\n",
    "**Return:** DataFrame containing first n rows\n",
    "\n",
    "**Choose the correct code:**\n",
    "- (a) `return df.iloc[:n]`\n",
    "- (b) `return df.head(n)`\n",
    "- (c) `return df.nlargest(n, axis=0)`\n",
    "- (d) `return df[:n:1]`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "a7c6666a",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_first_n_rows(df: pd.DataFrame, n: int) -> pd.DataFrame:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2fd74d3f",
   "metadata": {},
   "source": [
    "### 4. Get basic statistics for numeric columns\n",
    "**Return:** A pandas DataFrame with descriptive statistics (using .describe())\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "18326f48",
   "metadata": {},
   "outputs": [],
   "source": [
    "def describe_numeric(df: pd.DataFrame) -> pd.DataFrame:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93b7e373",
   "metadata": {},
   "source": [
    "### 5. Select a single column as a Series\n",
    "**Return:** A pandas Series for the specified column"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "46bac4c4",
   "metadata": {},
   "outputs": [],
   "source": [
    "def select_column(df: pd.DataFrame, col_name: str) -> pd.Series:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdf36936",
   "metadata": {},
   "source": [
    "### 6. Filter rows where a column value exceeds a threshold\n",
    "**Return:** A DataFrame containing only rows where column > threshold\n",
    "\n",
    "**Hint:** Use boolean indexing `df[df[col_name] > threshold]` and `.reset_index(drop=True)` to reset row indices.\n",
    "\n",
    "**Choose the correct code:**\n",
    "- (a) `return df.filter(column=col_name, value=threshold)`\n",
    "- (b) `return df.loc[df[col_name] > threshold]`\n",
    "- (c) `return df[df[col_name] > threshold].reset_index(drop=True)`\n",
    "- (d) `return df.query(f'{col_name} > {threshold}')`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dcf00b4e",
   "metadata": {},
   "outputs": [],
   "source": [
    "def filter_by_threshold(df: pd.DataFrame, col_name: str, threshold: float) -> pd.DataFrame:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e70c9d29",
   "metadata": {},
   "source": [
    "### 7. Count missing (NaN) values in each column\n",
    "**Return:** A pandas Series with column names as index and count of NaN as values\n",
    "\n",
    "**Hint:** Use `.isnull().sum()` to count missing values in each column."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eb46b189",
   "metadata": {},
   "outputs": [],
   "source": [
    "def count_missing_values(df: pd.DataFrame) -> pd.Series:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2efdd80f",
   "metadata": {},
   "source": [
    "### 8. Drop rows containing any NaN values\n",
    "**Return:** A DataFrame with all rows containing NaN removed\n",
    "\n",
    "**Hint:** Use `.dropna()` to remove rows with missing values, then `.reset_index(drop=True)` to renumber rows."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "81bea45f",
   "metadata": {},
   "outputs": [],
   "source": [
    "def drop_rows_with_nan(df: pd.DataFrame) -> pd.DataFrame:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0ffbdd4",
   "metadata": {},
   "source": [
    "### 9. Fill missing values with the mean of the column\n",
    "**Return:** A DataFrame where NaN values in numeric columns are replaced by column mean\n",
    "\n",
    "**Hint:** Get numeric columns using `.select_dtypes()`, then use `.fillna()` with the column mean."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98dc4cae",
   "metadata": {},
   "outputs": [],
   "source": [
    "def fill_missing_with_mean(df: pd.DataFrame) -> pd.DataFrame:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a799ecd6",
   "metadata": {},
   "source": [
    "### 10. Group by a column and calculate the mean of another column\n",
    "**Return:** A DataFrame with grouped results (group column and mean)\n",
    "\n",
    "**Hint:** Use `.groupby(group_col)[agg_col].mean()` and `.reset_index()` to convert to DataFrame."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9eb9b6a6",
   "metadata": {},
   "outputs": [],
   "source": [
    "def group_by_mean(df: pd.DataFrame, group_col: str, agg_col: str) -> pd.DataFrame:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9bf612b8",
   "metadata": {},
   "source": [
    "### 11. Merge two DataFrames on a common column\n",
    "**Return:** A merged DataFrame (inner join on the specified key)\n",
    "\n",
    "**Choose the correct code:**\n",
    "- (a) `return left.join(right, on=on)`\n",
    "- (b) `return pd.concat([left, right])`\n",
    "- (c) `return pd.merge(left, right, on=on, how='inner')`\n",
    "- (d) `return left.combine(right)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "7fc876a4",
   "metadata": {},
   "outputs": [],
   "source": [
    "def merge_dataframes(left: pd.DataFrame, right: pd.DataFrame, on: str) -> pd.DataFrame:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4a54a39",
   "metadata": {},
   "source": [
    "### 12. Convert a column to datetime format\n",
    "**Return:** A DataFrame where the specified column has been converted to datetime\n",
    "\n",
    "**Choose the correct code:**\n",
    "- (a) `df_copy[col_name] = df_copy[col_name].astype(datetime)`\n",
    "- (b) `df_copy[col_name] = pd.to_datetime(df_copy[col_name])`\n",
    "- (c) `df_copy[col_name].convert_to_datetime()`\n",
    "- (d) `df_copy[col_name] = datetime.strptime(df_copy[col_name], '%Y-%m-%d')`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2d78dfe7",
   "metadata": {},
   "outputs": [],
   "source": [
    "def convert_to_datetime(df: pd.DataFrame, col_name: str) -> pd.DataFrame:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42b7f823",
   "metadata": {},
   "source": [
    "### 13. Drop Duplicate Rows\n",
    "You have a DataFrame with duplicate rows. The command `drop_duplicates` on subset of columns named `['Name', 'Team']` is to be used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "737538a6",
   "metadata": {},
   "outputs": [],
   "source": [
    "def drop_duplicates_by_cols(df: pd.DataFrame) -> pd.DataFrame:\n",
    "    pass\n",
    "sample_df = pd.DataFrame({'Name': ['Alice', 'Bob', 'Alice', 'Charlie'], 'Team': ['X', 'Y', 'X', 'Z'], 'Salary': [50000, 55000, 50000, 60000]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e8b7e9cd",
   "metadata": {},
   "source": [
    "### 14. Fill Missing Values in a Column\n",
    "Write a Python command to fill all missing values in the column 'College' with the text 'Unknown'."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21851dea",
   "metadata": {},
   "outputs": [],
   "source": [
    "def fill_missing_college(df: pd.DataFrame) -> pd.DataFrame:\n",
    "    pass\n",
    "# sample_df = pd.DataFrame({'Name': ['Alice', 'Bob', 'Charlie'], 'College': ['IIT', None, 'NIT']})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95917896",
   "metadata": {},
   "source": [
    "### 15. Create New Column with Percentage Increase\n",
    "Given a DataFrame df with a 'Salary' column, write code to increase salary by 5% and store it in a new column 'UpdatedSalary'.\n",
    "\n",
    "**Hint:** Multiply the Salary column by 1.05 to increase by 5%.\n",
    "\n",
    "**Choose the correct code:**\n",
    "- (a) `df['UpdatedSalary'] = df['Salary'] * 5`\n",
    "- (b) `df['UpdatedSalary'] = df['Salary'] * 1.05`\n",
    "- (c) `df['UpdatedSalary'] = df['Salary'] + 0.05`\n",
    "- (d) `df['UpdatedSalary'] = df['Salary'].apply(lambda x: x * 5)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "03a08b07",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add_updated_salary(df: pd.DataFrame) -> pd.DataFrame:\n",
    "    pass\n",
    "# sample_df = pd.DataFrame({'Name': ['Alice', 'Bob', 'Charlie'], 'Salary': [50000, 55000, 60000]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ecd58c7",
   "metadata": {},
   "source": [
    "### 16. Filter Rows with Range Condition\n",
    "Write Python code to select rows where 'Profit' is between 30 and 55 (inclusive).\n",
    "\n",
    "**Hint:** Use boolean indexing with AND operator `&`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fa937d6f",
   "metadata": {},
   "outputs": [],
   "source": [
    "def filter_profit_range(df: pd.DataFrame) -> pd.DataFrame:\n",
    "    pass\n",
    "# sample_df = pd.DataFrame({'Name': ['A', 'B', 'C', 'D'], 'Profit': [25, 40, 60, 35]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d602461",
   "metadata": {},
   "source": [
    "### 17. Get Summary Statistics\n",
    "Write a Python command to show summary statistics (mean, median, std, min, max, etc.) for the entire DataFrame."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6053e461",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_summary_stats(df: pd.DataFrame) -> pd.DataFrame:\n",
    "    pass\n",
    "# sample_df = pd.DataFrame({'Age': [25, 30, 28, 35], 'Salary': [50000, 55000, 52000, 60000]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d320b53",
   "metadata": {},
   "source": [
    "### 18. GroupBy with Multiple Aggregations\n",
    "You have a DataFrame with columns: Name, Team, Salary, Profit\n",
    "\n",
    "Write Python code to:\n",
    "1. Group the data by Team\n",
    "2. aggregate average salary and total profit for each team\n",
    "3. return the result\n",
    "\n",
    "**Hint:** Use `.groupby()` with `.agg()` for multiple aggregations.\n",
    "\n",
    "**Choose the correct code:**\n",
    "- (a) `df.groupby('Team').agg({'Salary': 'mean', 'Profit': 'sum'})`\n",
    "- (b) `df.groupby('Team')[['Salary', 'Profit']].agg(['mean', 'sum'])`\n",
    "- (c) `df.group('Team').apply(lambda x: {'avg_salary': x['Salary'].mean(), 'total_profit': x['Profit'].sum()})`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "51fd5a32",
   "metadata": {},
   "outputs": [],
   "source": [
    "def groupby_team_agg(df: pd.DataFrame) -> pd.DataFrame:\n",
    "    pass\n",
    "# sample_df = pd.DataFrame({'Name': ['A', 'B', 'C', 'D'], 'Team': ['X', 'X', 'Y', 'Y'], 'Salary': [50000, 55000, 52000, 53000], 'Profit': [45, 30, 60, 25]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "129962e0",
   "metadata": {},
   "source": [
    "### 19. Advanced Filtering and Column Creation\n",
    "Given a DataFrame with columns: Name, Score1, Score2\n",
    "\n",
    "Write Python code to:\n",
    "1. Select only rows where Score1 > 40 AND Score2 > 50\n",
    "2. Create a new column AverageScore = mean of Score1 and Score2\n",
    "3. return dataframe with only the  `[['Name', 'AverageScore']]`\n",
    "**Hint:** Filter first using boolean indexing, then add the new column, then select specific columns."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a1e8de3a",
   "metadata": {},
   "outputs": [],
   "source": [
    "def advanced_filter_and_create(df: pd.DataFrame) -> pd.DataFrame:\n",
    "    pass\n",
    "# sample_df = pd.DataFrame({'Name': ['A', 'B', 'C', 'D'], 'Score1': [40, 55, 70, 30], 'Score2': [50, 65, 75, 35]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9ef8914e",
   "metadata": {},
   "source": [
    "### 20. Handle Missing Values and Outliers\n",
    "You have a DataFrame with an 'Age' column containing missing values and outliers (Age > 100).\n",
    "\n",
    "Write Python code to:\n",
    "1. Replace missing values with the median age\n",
    "2. Remove rows where Age > 100\n",
    "3. Return the cleaned DataFrame\n",
    "\n",
    "**Hint:** Use `.fillna()` with median, then filter with boolean indexing."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d46a7900",
   "metadata": {},
   "outputs": [],
   "source": [
    "def clean_age_data(df: pd.DataFrame) -> pd.DataFrame:\n",
    "    pass\n",
    "# sample_df = pd.DataFrame({'Name': ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve'], 'Age': [25, np.nan, 105, 30, np.nan]})"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv (3.13.7)",
   "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.13.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
