code: def pivot_longer(
      df: pd.DataFrame,
      index: Optional[Union[list, tuple, str, Pattern]] = None,
      column_names: Optional[Union[list, tuple, str, Pattern]] = None,
      names_to: Optional[Union[list, tuple, str]] = None,
      values_to: Optional[str] = "value",
      column_level: Optional[Union[int, str]] = None,
      names_sep: Optional[Union[str, Pattern]] = None,
      names_pattern: Optional[Union[list, tuple, str, Pattern]] = None,
      names_transform: Optional[Union[str, Callable, dict]] = None,
      dropna: bool = False,
      sort_by_appearance: Optional[bool] = False,
      ignore_index: Optional[bool] = True,
  ) -> pd.DataFrame:
      """Unpivots a DataFrame from *wide* to *long* format.
  
      This method does not mutate the original DataFrame.
  
      It is modeled after the `pivot_longer` function in R's tidyr package,
      and also takes inspiration from R's data.table package.
  
      This function is useful to massage a DataFrame into a format where
      one or more columns are considered measured variables, and all other
      columns are considered as identifier variables.
  
      All measured variables are *unpivoted* (and typically duplicated) along the
      row axis.
  
      Column selection in `index` and `column_names` is possible using the
      [`select`][janitor.functions.select.select] syntax.
  
      Examples:
          >>> import pandas as pd
          >>> import janitor
          >>> df = pd.DataFrame(
          ...     {
          ...         "Sepal.Length": [5.1, 5.9],
          ...         "Sepal.Width": [3.5, 3.0],
          ...         "Petal.Length": [1.4, 5.1],
          ...         "Petal.Width": [0.2, 1.8],
          ...         "Species": ["setosa", "virginica"],
          ...     }
          ... )
          >>> df
             Sepal.Length  Sepal.Width  Petal.Length  Petal.Width    Species
          0           5.1          3.5           1.4          0.2     setosa
          1           5.9          3.0           5.1          1.8  virginica
  
          Replicate pandas' melt:
          >>> df.pivot_longer(index = 'Species')
               Species      variable  value
          0     setosa  Sepal.Length    5.1
          1  virginica  Sepal.Length    5.9
          2     setosa   Sepal.Width    3.5
          3  virginica   Sepal.Width    3.0
          4     setosa  Petal.Length    1.4
          5  virginica  Petal.Length    5.1
          6     setosa   Petal.Width    0.2
          7  virginica   Petal.Width    1.8
  
          Convenient, flexible column selection in the `index` via the
          [`select`][janitor.functions.select.select] syntax:
          >>> from pandas.api.types import is_string_dtype
          >>> df.pivot_longer(index = is_string_dtype)
               Species      variable  value
          0     setosa  Sepal.Length    5.1
          1  virginica  Sepal.Length    5.9
          2     setosa   Sepal.Width    3.5
          3  virginica   Sepal.Width    3.0
          4     setosa  Petal.Length    1.4
          5  virginica  Petal.Length    5.1
          6     setosa   Petal.Width    0.2
          7  virginica   Petal.Width    1.8
  
          Split the column labels into parts:
          >>> df.pivot_longer(
          ...     index = 'Species',
          ...     names_to = ('part', 'dimension'),
          ...     names_sep = '.',
          ...     sort_by_appearance = True,
          ... )
               Species   part dimension  value
          0     setosa  Sepal    Length    5.1
          1     setosa  Sepal     Width    3.5
          2     setosa  Petal    Length    1.4
          3     setosa  Petal     Width    0.2
          4  virginica  Sepal    Length    5.9
          5  virginica  Sepal     Width    3.0
          6  virginica  Petal    Length    5.1
          7  virginica  Petal     Width    1.8
  
          Retain parts of the column names as headers:
          >>> df.pivot_longer(
          ...     index = 'Species',
          ...     names_to = ('part', '.value'),
          ...     names_sep = '.',
          ...     sort_by_appearance = True,
          ... )
               Species   part  Length  Width
          0     setosa  Sepal     5.1    3.5
          1     setosa  Petal     1.4    0.2
          2  virginica  Sepal     5.9    3.0
          3  virginica  Petal     5.1    1.8
  
          Split the column labels based on regex:
          >>> df = pd.DataFrame({"id": [1], "new_sp_m5564": [2], "newrel_f65": [3]})
          >>> df
             id  new_sp_m5564  newrel_f65
          0   1             2           3
          >>> df.pivot_longer(
          ...     index = 'id',
          ...     names_to = ('diagnosis', 'gender', 'age'),
          ...     names_pattern = r"new_?(.+)_(.)(\\d+)",
          ... )
             id diagnosis gender   age  value
          0   1        sp      m  5564      2
          1   1       rel      f    65      3
  
          Split the column labels for the above dataframe using named groups in `names_pattern`:
          >>> df.pivot_longer(
          ...     index = 'id',
          ...     names_pattern = r"new_?(?P<diagnosis>.+)_(?P<gender>.)(?P<age>\\d+)",
          ... )
              id diagnosis gender   age  value
          0   1        sp      m  5564      2
          1   1       rel      f    65      3
  
          Convert the dtypes of specific columns with `names_transform`:
          >>> result = (df
          ...          .pivot_longer(
          ...              index = 'id',
          ...              names_to = ('diagnosis', 'gender', 'age'),
          ...              names_pattern = r"new_?(.+)_(.)(\\d+)",
          ...              names_transform = {'gender': 'category', 'age':'int'})
          ... )
          >>> result.dtypes
          id           int64
          gender    category
          age          int64
          value        int64
          dtype: object
  
          Use multiple `.value` to reshape dataframe:
          >>> df = pd.DataFrame(
          ...     [
          ...         {
          ...             "x_1_mean": 10,
          ...             "x_2_mean": 20,
          ...             "y_1_mean": 30,
          ...             "y_2_mean": 40,
          ...             "unit": 50,
          ...         }
          ...     ]
          ... )
          >>> df
             x_1_mean  x_2_mean  y_1_mean  y_2_mean  unit
          0        10        20        30        40    50
          >>> df.pivot_longer(
          ...     index="unit",
          ...     names_to=(".value", "time", ".value"),
          ...     names_pattern=r"(x|y)_([0-9])(_mean)",
          ... )
             unit time  x_mean  y_mean
          0    50    1      10      30
          1    50    2      20      40
  
          Replicate the above with named groups in `names_pattern` - use `_` instead of `.value`:
          >>> df.pivot_longer(
          ...     index="unit",
          ...     names_pattern=r"(?P<_>x|y)_(?P<time>[0-9])(?P<__>_mean)",
          ... )
             unit time  x_mean  y_mean
          0    50    1      10      30
          1    50    2      20      40
  
          Convenient, flexible column selection in the `column_names` via
          [`select`][janitor.functions.select.select] syntax:
          >>> df.pivot_longer(
          ...     column_names="*mean",
          ...     names_to=(".value", "time", ".value"),
          ...     names_pattern=r"(x|y)_([0-9])(_mean)",
          ... )
             unit time  x_mean  y_mean
          0    50    1      10      30
          1    50    2      20      40
  
          >>> df.pivot_longer(
          ...     column_names=slice("x_1_mean", "y_2_mean"),
          ...     names_to=(".value", "time", ".value"),
          ...     names_pattern=r"(x|y)_([0-9])(_mean)",
          ... )
             unit time  x_mean  y_mean
          0    50    1      10      30
          1    50    2      20      40
  
          Reshape dataframe by passing a sequence to `names_pattern`:
          >>> df = pd.DataFrame({'hr1': [514, 573],
          ...                    'hr2': [545, 526],
          ...                    'team': ['Red Sox', 'Yankees'],
          ...                    'year1': [2007, 2007],
          ...                    'year2': [2008, 2008]})
          >>> df
             hr1  hr2     team  year1  year2
          0  514  545  Red Sox   2007   2008
          1  573  526  Yankees   2007   2008
          >>> df.pivot_longer(
          ...     index = 'team',
          ...     names_to = ['year', 'hr'],
          ...     names_pattern = ['year', 'hr']
          ... )
                team   hr  year
          0  Red Sox  514  2007
          1  Yankees  573  2007
          2  Red Sox  545  2008
          3  Yankees  526  2008
  
  
          Reshape above dataframe by passing a dictionary to `names_pattern`:
          >>> df.pivot_longer(
          ...     index = 'team',
          ...     names_pattern = {"year":"year", "hr":"hr"}
          ... )
                team   hr  year
          0  Red Sox  514  2007
          1  Yankees  573  2007
          2  Red Sox  545  2008
          3  Yankees  526  2008
  
          Multiple values_to:
          >>> df = pd.DataFrame(
          ...         {
          ...             "City": ["Houston", "Austin", "Hoover"],
          ...             "State": ["Texas", "Texas", "Alabama"],
          ...             "Name": ["Aria", "Penelope", "Niko"],
          ...             "Mango": [4, 10, 90],
          ...             "Orange": [10, 8, 14],
          ...             "Watermelon": [40, 99, 43],
          ...             "Gin": [16, 200, 34],
          ...             "Vodka": [20, 33, 18],
          ...         },
          ...         columns=[
          ...             "City",
          ...             "State",
          ...             "Name",
          ...             "Mango",
          ...             "Orange",
          ...             "Watermelon",
          ...             "Gin",
          ...             "Vodka",
          ...         ],
          ...     )
          >>> df
                City    State      Name  Mango  Orange  Watermelon  Gin  Vodka
          0  Houston    Texas      Aria      4      10          40   16     20
          1   Austin    Texas  Penelope     10       8          99  200     33
          2   Hoover  Alabama      Niko     90      14          43   34     18
          >>> df.pivot_longer(
          ...         index=["City", "State"],
          ...         column_names=slice("Mango", "Vodka"),
          ...         names_to=("Fruit", "Drink"),
          ...         values_to=("Pounds", "Ounces"),
          ...         names_pattern=["M|O|W", "G|V"],
          ...     )
                City    State       Fruit  Pounds  Drink  Ounces
          0  Houston    Texas       Mango       4    Gin    16.0
          1   Austin    Texas       Mango      10    Gin   200.0
          2   Hoover  Alabama       Mango      90    Gin    34.0
          3  Houston    Texas      Orange      10  Vodka    20.0
          4   Austin    Texas      Orange       8  Vodka    33.0
          5   Hoover  Alabama      Orange      14  Vodka    18.0
          6  Houston    Texas  Watermelon      40   None     NaN
          7   Austin    Texas  Watermelon      99   None     NaN
          8   Hoover  Alabama  Watermelon      43   None     NaN
  
          Replicate the above transformation with a nested dictionary passed to `names_pattern`
          - the outer keys in the `names_pattern` dictionary are passed to `names_to`,
          while the inner keys are passed to `values_to`:
          >>> df.pivot_longer(
          ...     index=["City", "State"],
          ...     column_names=slice("Mango", "Vodka"),
          ...     names_pattern={
          ...         "Fruit": {"Pounds": "M|O|W"},
          ...         "Drink": {"Ounces": "G|V"},
          ...     },
          ... )
                City    State       Fruit  Pounds  Drink  Ounces
          0  Houston    Texas       Mango       4    Gin    16.0
          1   Austin    Texas       Mango      10    Gin   200.0
          2   Hoover  Alabama       Mango      90    Gin    34.0
          3  Houston    Texas      Orange      10  Vodka    20.0
          4   Austin    Texas      Orange       8  Vodka    33.0
          5   Hoover  Alabama      Orange      14  Vodka    18.0
          6  Houston    Texas  Watermelon      40   None     NaN
          7   Austin    Texas  Watermelon      99   None     NaN
          8   Hoover  Alabama  Watermelon      43   None     NaN
  
      !!! abstract "Version Changed"
  
          - 0.24.0
              - Added `dropna` parameter.
          - 0.24.1
              - `names_pattern` can accept a dictionary.
              - named groups supported in `names_pattern`.
  
      Args:
          df: A pandas DataFrame.
          index: Name(s) of columns to use as identifier variables.
              Should be either a single column name, or a list/tuple of
              column names.
              `index` should be a list of tuples if the columns are a MultiIndex.
          column_names: Name(s) of columns to unpivot. Should be either
              a single column name or a list/tuple of column names.
              `column_names` should be a list of tuples
              if the columns are a MultiIndex.
          names_to: Name of new column as a string that will contain
              what were previously the column names in `column_names`.
              The default is `variable` if no value is provided. It can
              also be a list/tuple of strings that will serve as new column
              names, if `name_sep` or `names_pattern` is provided.
              If `.value` is in `names_to`, new column names will be extracted
              from part of the existing column names and overrides`values_to`.
          values_to: Name of new column as a string that will contain what
              were previously the values of the columns in `column_names`.
              values_to can also be a list/tuple
              and requires that names_pattern is also a list/tuple.
          column_level: If columns are a MultiIndex, then use this level to
              unpivot the DataFrame. Provided for compatibility with pandas' melt,
              and applies only if neither `names_sep` nor `names_pattern` is
              provided.
          names_sep: Determines how the column name is broken up, if
              `names_to` contains multiple values. It takes the same
              specification as pandas' `str.split` method, and can be a string
              or regular expression. `names_sep` does not work with MultiIndex
              columns.
          names_pattern: Determines how the column name is broken up.
              It can be a regular expression containing matching groups.
              Under the hood it is processed with pandas' `str.extract` function.
              If it is a single regex, the number of groups must match
              the length of `names_to`.
              Named groups are supported, if `names_to` is none. `_` is used
              instead of `.value` as a placeholder in named groups.
              `_` can be overloaded for multiple `.value`
              calls - `_`, `__`, `___`, ...
              `names_pattern` can also be a list/tuple of regular expressions
              It can also be a list/tuple of strings;
              the strings will be treated as regular expressions.
              Under the hood it is processed with pandas' `str.contains` function.
              For a list/tuple of regular expressions,
              `names_to` must also be a list/tuple and the lengths of both
              arguments must match.
              `names_pattern` can also be a dictionary, where the keys are
              the new column names, while the values can be a regular expression
              or a string which will be evaluated as a regular expression.
              Alternatively, a nested dictionary can be used, where the sub
              key(s) are associated with `values_to`. Please have a look
              at the examples for usage.
              `names_pattern` does not work with MultiIndex columns.
          names_transform: Use this option to change the types of columns that
              have been transformed to rows. This does not applies to the values' columns.
              Accepts any argument that is acceptable by `pd.astype`.
          dropna: Determines whether or not to drop nulls
              from the values columns. Default is `False`.
          sort_by_appearance: Boolean value that determines
              the final look of the DataFrame. If `True`, the unpivoted DataFrame
              will be stacked in order of first appearance.
          ignore_index: If `True`,
              the original index is ignored. If `False`, the original index
              is retained and the index labels will be repeated as necessary.
  
      Returns:
          A pandas DataFrame that has been unpivoted from wide to long
              format.
      """  # noqa: E501
  
      # this code builds on the wonderful work of @benjaminjack’s PR
      # https://github.com/benjaminjack/pyjanitor/commit/e3df817903c20dd21634461c8a92aec137963ed0
  
      (
          df,
          index,
          column_names,
          names_to,
          values_to,
          names_sep,
          names_pattern,
          names_transform,
          dropna,
          sort_by_appearance,
          ignore_index,
      ) = _data_checks_pivot_longer(
          df,
          index,
          column_names,
          names_to,
          values_to,
          column_level,
          names_sep,
          names_pattern,
          names_transform,
          dropna,
          sort_by_appearance,
          ignore_index,
      )
  
      return _computations_pivot_longer(
          df,
          index,
          column_names,
          names_to,
          values_to,
          names_sep,
          names_pattern,
          names_transform,
          dropna,
          sort_by_appearance,
          ignore_index,
      )
  
imports: import re
  import warnings
  from collections import defaultdict
  from itertools import chain, zip_longest
  from typing import Callable, Optional, Pattern, Union
  import numpy
  import pandas
  import pandas_flavor
  from pandas.api.types import is_categorical_dtype, is_extension_array_dtype, is_list_like
  from pandas.core.dtypes.concat import concat_compat
  from janitor.functions.utils import _computations_expand_grid, get_index_labels
  from janitor.utils import check, refactored_function
label_desc: Use image node_id 1 for calling a global function with example usage: pivot_longer(df, index, column_names, names_to, values_to, column_level, names_sep, names_pattern, names_transform, dropna, sort_by_appearance, ignore_index) and returns: _computations_pivot_longer