docs: def extract_xyz_matrix_from_loop_json(pdb_lines, parsed_loop_json_contents, atoms_of_interest = backbone_atoms, expected_num_residues = None, expected_num_residue_atoms = None, allow_overlaps = False, include_all_columns = False):
          '''A utility wrapper to extract_xyz_matrix_from_pdb_residue_range.
             This accepts PDB file lines and a loop.json file (a defined Rosetta format) and returns a pandas dataframe of
             the X, Y, Z coordinates for the requested atom types for all residues in all loops defined by the loop.json
             file. The dataframe is indexed by a string identifying the PDB residue and atom type.
  
             loop_json_contents should be a Python dict read in from a loop.json file e.g. json.loads(file_contents).'''
  
          # Create one dataframe per loop segment
          dataframes = []
          for loop_set in parsed_loop_json_contents['LoopSet']:
              start_pdb_residue_id = PDB.ChainResidueID2String(loop_set['start']['chainID'], str(loop_set['start']['resSeq']) + loop_set['start']['iCode'])
              stop_pdb_residue_id = PDB.ChainResidueID2String(loop_set['stop']['chainID'], str(loop_set['stop']['resSeq']) + loop_set['stop']['iCode'])
              dataframes.append(PDB.extract_xyz_matrix_from_pdb_residue_range(pdb_lines, start_pdb_residue_id = start_pdb_residue_id, stop_pdb_residue_id = stop_pdb_residue_id, atoms_of_interest = atoms_of_interest, expected_num_residues = None, expected_num_residue_atoms = expected_num_residue_atoms, include_all_columns = include_all_columns))
  
          # Concatenate the dataframes
          dataframe = pandas.concat(dataframes, verify_integrity = (allow_overlaps == False)) # note: the pandas documentation notes that verify_integrity is relatively expensive
          if expected_num_residues != None and expected_num_residue_atoms != None:
              assert(dataframe.shape[0] == expected_num_residues * expected_num_residue_atoms)
          return dataframe