Boolean Combinations of Gates

This notebook demonstrates how to make a set of Boolean gates from a set of gates that define positive populations. For example, consider we have positive populations for 3 markers: A, B, and C. The goal is to get the set of all 2**3 combinations:

A+ B+ C+
A- B+ C+
A+ B- C+
A+ B+ C-
A- B- C+
A- B+ C-
A+ B- C-
A- B- C-

We’ll do this using 2 different methods. First, using BooleanGate objects within a Session. And, since FlowKit Workspace objects are read-only, we’ll extract the Boolean gate membership arrays from the analyzed results and perform the operations on those arrays directly.

[1]:
import itertools
import os
import pickle
import numpy as np
import pandas as pd

import flowkit as fk

%matplotlib inline

Session method using BooleanGate objects

[2]:
base_dir = "../../../data/8_color_data_set"

sample_path = os.path.join(base_dir, "fcs_files")
session_path = os.path.join(base_dir, "8_color_ICS_session.pkl")

Import previous Session (see replicate-flowjo-wsp.ipynb)

In a previous tutorial we programmatically built a gating strategy using the Session class. We’ll use that gate tree here to make a set of Boolean gates for the set of cytokine populations.

[3]:
with open(session_path, 'rb') as f:
    session = pickle.load(f)

Preview loaded Sample IDs & gate tree

[4]:
sample_ids = sorted(session.get_sample_ids())
[5]:
sample_ids
[5]:
['101_DEN084Y5_15_E01_008_clean.fcs',
 '101_DEN084Y5_15_E03_009_clean.fcs',
 '101_DEN084Y5_15_E05_010_clean.fcs']
[6]:
print(session.get_gate_hierarchy())
root
╰── Time
    ╰── Singlets
        ╰── aAmine-
            ╰── CD3+
                ├── CD4+
                │   ├── CD107a+
                │   ├── IFNg+
                │   ├── IL2+
                │   ╰── TNFa+
                ╰── CD8+
                    ├── CD107a+
                    ├── IFNg+
                    ├── IL2+
                    ╰── TNFa+

Gather info for CD4+ cytokine gates

[7]:
cd4_child_gate_ids = session.get_child_gate_ids('CD4+')
[8]:
cd4_child_gate_ids
[8]:
[('CD107a+', ('root', 'Time', 'Singlets', 'aAmine-', 'CD3+', 'CD4+')),
 ('IFNg+', ('root', 'Time', 'Singlets', 'aAmine-', 'CD3+', 'CD4+')),
 ('IL2+', ('root', 'Time', 'Singlets', 'aAmine-', 'CD3+', 'CD4+')),
 ('TNFa+', ('root', 'Time', 'Singlets', 'aAmine-', 'CD3+', 'CD4+'))]
[9]:
cd4_gate_path = cd4_child_gate_ids[0][1]
[10]:
cd4_gate_path
[10]:
('root', 'Time', 'Singlets', 'aAmine-', 'CD3+', 'CD4+')
[11]:
cd4_child_gate_names = [i[0] for i in cd4_child_gate_ids]
[12]:
cd4_child_gate_names
[12]:
['CD107a+', 'IFNg+', 'IL2+', 'TNFa+']

Build Boolean combo data structures

[13]:
n_channels = len(cd4_child_gate_ids)
bool_combos = list(itertools.product([True, False], repeat=n_channels))
[14]:
bool_combos
[14]:
[(True, True, True, True),
 (True, True, True, False),
 (True, True, False, True),
 (True, True, False, False),
 (True, False, True, True),
 (True, False, True, False),
 (True, False, False, True),
 (True, False, False, False),
 (False, True, True, True),
 (False, True, True, False),
 (False, True, False, True),
 (False, True, False, False),
 (False, False, True, True),
 (False, False, True, False),
 (False, False, False, True),
 (False, False, False, False)]
[15]:
def generate_bool_gate_name(gate_labels, bool_list, prefix=None):
    # convert Boolean values to strings '+' or '-'
    bool_strings = ['+' if b else '-' for b in bool_list]

    # Our gate names have a trailing '+'
    # Remove these so we can append '+' or '-' as appropriate for each bool combo
    new_gate_labels = [l[:-1] if l[-1] == '+' else l for l in gate_labels]

    # Now, map the trailing '+' or '-' to each item, matching the bool list
    new_gate_labels = list(map(lambda gate_label, b: gate_label + b, new_gate_labels, bool_strings))

    # Start building the new gate name for the entire set,
    # starting with the optional prefix
    if prefix is not None:
        gate_name = prefix + ':'
    else:
        gate_name = ''

    return gate_name + "_".join(new_gate_labels)
[16]:
bool_gate_labels = []

for bc in bool_combos:
    tmp_label = generate_bool_gate_name(cd4_child_gate_names, bc, prefix='CD4')
    bool_gate_labels.append(tmp_label)
[17]:
bool_gate_labels
[17]:
['CD4:CD107a+_IFNg+_IL2+_TNFa+',
 'CD4:CD107a+_IFNg+_IL2+_TNFa-',
 'CD4:CD107a+_IFNg+_IL2-_TNFa+',
 'CD4:CD107a+_IFNg+_IL2-_TNFa-',
 'CD4:CD107a+_IFNg-_IL2+_TNFa+',
 'CD4:CD107a+_IFNg-_IL2+_TNFa-',
 'CD4:CD107a+_IFNg-_IL2-_TNFa+',
 'CD4:CD107a+_IFNg-_IL2-_TNFa-',
 'CD4:CD107a-_IFNg+_IL2+_TNFa+',
 'CD4:CD107a-_IFNg+_IL2+_TNFa-',
 'CD4:CD107a-_IFNg+_IL2-_TNFa+',
 'CD4:CD107a-_IFNg+_IL2-_TNFa-',
 'CD4:CD107a-_IFNg-_IL2+_TNFa+',
 'CD4:CD107a-_IFNg-_IL2+_TNFa-',
 'CD4:CD107a-_IFNg-_IL2-_TNFa+',
 'CD4:CD107a-_IFNg-_IL2-_TNFa-']

Create the BooleanGate instances

[18]:
bool_gates = []

for i, bool_gate_name in enumerate(bool_gate_labels):
    # boolean gates need gate references
    # one for each of the 4 gates
    # and the 'complement' controls the True/False or +/-
    # where complement=True is the inverse, so the - of our + gates
    gate_refs = []

    # and grab the specific bool combo for this new bool gate label
    bool_combo = bool_combos[i]

    for j, child_gate_name in enumerate(cd4_child_gate_names):
        gate_ref = {
            'ref': child_gate_name,
            'path': cd4_gate_path,
            'complement': not bool_combo[j]
        }
        gate_refs.append(gate_ref)

    bool_gate = fk.gates.BooleanGate(
        bool_gate_name,
        'and',
        gate_refs
    )
    bool_gates.append(bool_gate)
[19]:
bool_gates
[19]:
[BooleanGate(CD4:CD107a+_IFNg+_IL2+_TNFa+, type: and),
 BooleanGate(CD4:CD107a+_IFNg+_IL2+_TNFa-, type: and),
 BooleanGate(CD4:CD107a+_IFNg+_IL2-_TNFa+, type: and),
 BooleanGate(CD4:CD107a+_IFNg+_IL2-_TNFa-, type: and),
 BooleanGate(CD4:CD107a+_IFNg-_IL2+_TNFa+, type: and),
 BooleanGate(CD4:CD107a+_IFNg-_IL2+_TNFa-, type: and),
 BooleanGate(CD4:CD107a+_IFNg-_IL2-_TNFa+, type: and),
 BooleanGate(CD4:CD107a+_IFNg-_IL2-_TNFa-, type: and),
 BooleanGate(CD4:CD107a-_IFNg+_IL2+_TNFa+, type: and),
 BooleanGate(CD4:CD107a-_IFNg+_IL2+_TNFa-, type: and),
 BooleanGate(CD4:CD107a-_IFNg+_IL2-_TNFa+, type: and),
 BooleanGate(CD4:CD107a-_IFNg+_IL2-_TNFa-, type: and),
 BooleanGate(CD4:CD107a-_IFNg-_IL2+_TNFa+, type: and),
 BooleanGate(CD4:CD107a-_IFNg-_IL2+_TNFa-, type: and),
 BooleanGate(CD4:CD107a-_IFNg-_IL2-_TNFa+, type: and),
 BooleanGate(CD4:CD107a-_IFNg-_IL2-_TNFa-, type: and)]
[20]:
bool_gates[5].gate_name
[20]:
'CD4:CD107a+_IFNg-_IL2+_TNFa-'
[21]:
bool_gates[5].gate_refs
[21]:
[{'ref': 'CD107a+',
  'path': ('root', 'Time', 'Singlets', 'aAmine-', 'CD3+', 'CD4+'),
  'complement': False},
 {'ref': 'IFNg+',
  'path': ('root', 'Time', 'Singlets', 'aAmine-', 'CD3+', 'CD4+'),
  'complement': True},
 {'ref': 'IL2+',
  'path': ('root', 'Time', 'Singlets', 'aAmine-', 'CD3+', 'CD4+'),
  'complement': False},
 {'ref': 'TNFa+',
  'path': ('root', 'Time', 'Singlets', 'aAmine-', 'CD3+', 'CD4+'),
  'complement': True}]

Add new gates to Session

[22]:
for bool_gate in bool_gates:
    session.add_gate(bool_gate, cd4_gate_path)
[23]:
print(session.get_gate_hierarchy())
root
╰── Time
    ╰── Singlets
        ╰── aAmine-
            ╰── CD3+
                ├── CD4+
                │   ├── CD107a+
                │   ├── IFNg+
                │   ├── IL2+
                │   ├── TNFa+
                │   ├── CD4:CD107a+_IFNg+_IL2+_TNFa+
                │   ├── CD4:CD107a+_IFNg+_IL2+_TNFa-
                │   ├── CD4:CD107a+_IFNg+_IL2-_TNFa+
                │   ├── CD4:CD107a+_IFNg+_IL2-_TNFa-
                │   ├── CD4:CD107a+_IFNg-_IL2+_TNFa+
                │   ├── CD4:CD107a+_IFNg-_IL2+_TNFa-
                │   ├── CD4:CD107a+_IFNg-_IL2-_TNFa+
                │   ├── CD4:CD107a+_IFNg-_IL2-_TNFa-
                │   ├── CD4:CD107a-_IFNg+_IL2+_TNFa+
                │   ├── CD4:CD107a-_IFNg+_IL2+_TNFa-
                │   ├── CD4:CD107a-_IFNg+_IL2-_TNFa+
                │   ├── CD4:CD107a-_IFNg+_IL2-_TNFa-
                │   ├── CD4:CD107a-_IFNg-_IL2+_TNFa+
                │   ├── CD4:CD107a-_IFNg-_IL2+_TNFa-
                │   ├── CD4:CD107a-_IFNg-_IL2-_TNFa+
                │   ╰── CD4:CD107a-_IFNg-_IL2-_TNFa-
                ╰── CD8+
                    ├── CD107a+
                    ├── IFNg+
                    ├── IL2+
                    ╰── TNFa+

Re-analyze samples with new Boolean gates

[24]:
session.analyze_samples()
[25]:
report = session.get_analysis_report()
[26]:
report_cols = list(report.columns)
report_cols.remove('gate_path')
report_cols.remove('quadrant_parent')

report[
    (report['sample_id'] == '101_DEN084Y5_15_E01_008_clean.fcs') &
    (
        (report['parent'] == 'CD4+') |
        (report['gate_name'] == 'CD4+')
    )
][report_cols]
[26]:
sample_id gate_name gate_type parent count absolute_percent relative_percent level
34 101_DEN084Y5_15_E01_008_clean.fcs CD4+ PolygonGate CD3+ 74407 25.642378 61.780848 5
36 101_DEN084Y5_15_E01_008_clean.fcs CD107a+ RectangleGate CD4+ 56 0.019299 0.075262 6
38 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a+_IFNg+_IL2+_TNFa+ BooleanGate CD4+ 0 0.000000 0.000000 6
39 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a+_IFNg+_IL2+_TNFa- BooleanGate CD4+ 0 0.000000 0.000000 6
40 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a+_IFNg+_IL2-_TNFa+ BooleanGate CD4+ 0 0.000000 0.000000 6
41 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a+_IFNg+_IL2-_TNFa- BooleanGate CD4+ 0 0.000000 0.000000 6
42 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a+_IFNg-_IL2+_TNFa+ BooleanGate CD4+ 0 0.000000 0.000000 6
43 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a+_IFNg-_IL2+_TNFa- BooleanGate CD4+ 0 0.000000 0.000000 6
44 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a+_IFNg-_IL2-_TNFa+ BooleanGate CD4+ 0 0.000000 0.000000 6
45 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a+_IFNg-_IL2-_TNFa- BooleanGate CD4+ 56 0.019299 0.075262 6
46 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a-_IFNg+_IL2+_TNFa+ BooleanGate CD4+ 0 0.000000 0.000000 6
47 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a-_IFNg+_IL2+_TNFa- BooleanGate CD4+ 0 0.000000 0.000000 6
48 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a-_IFNg+_IL2-_TNFa+ BooleanGate CD4+ 4 0.001378 0.005376 6
49 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a-_IFNg+_IL2-_TNFa- BooleanGate CD4+ 0 0.000000 0.000000 6
50 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a-_IFNg-_IL2+_TNFa+ BooleanGate CD4+ 1 0.000345 0.001344 6
51 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a-_IFNg-_IL2+_TNFa- BooleanGate CD4+ 3 0.001034 0.004032 6
52 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a-_IFNg-_IL2-_TNFa+ BooleanGate CD4+ 15 0.005169 0.020159 6
53 101_DEN084Y5_15_E01_008_clean.fcs CD4:CD107a-_IFNg-_IL2-_TNFa- BooleanGate CD4+ 74328 25.615152 99.893827 6
54 101_DEN084Y5_15_E01_008_clean.fcs IFNg+ RectangleGate CD4+ 4 0.001378 0.005376 6
56 101_DEN084Y5_15_E01_008_clean.fcs IL2+ RectangleGate CD4+ 4 0.001378 0.005376 6
58 101_DEN084Y5_15_E01_008_clean.fcs TNFa+ RectangleGate CD4+ 20 0.006892 0.026879 6

Workspace method using Boolean gate membership arrays

Import FlowJo 10 workspace & preview gate info

[27]:
wsp_path = os.path.join(base_dir, "8_color_ICS.wsp")
workspace = fk.Workspace(wsp_path, fcs_samples=sample_path)
[28]:
workspace.summary()
[28]:
samples loaded_samples gates max_gate_depth
group_name
All Samples 3 3 14 6
DEN 3 3 14 6
GEN 0 0 0 0
G69 0 0 0 0
Lyo Cells 0 0 0 0
[29]:
sample_ids = workspace.get_sample_ids()
[30]:
sample_ids
[30]:
['101_DEN084Y5_15_E01_008_clean.fcs',
 '101_DEN084Y5_15_E03_009_clean.fcs',
 '101_DEN084Y5_15_E05_010_clean.fcs']
[31]:
print(workspace.get_gate_hierarchy(sample_id=sample_ids[0]))
root
╰── Time
    ╰── Singlets
        ╰── aAmine-
            ╰── CD3+
                ├── CD4+
                │   ├── CD107a+
                │   ├── IFNg+
                │   ├── IL2+
                │   ╰── TNFa+
                ╰── CD8+
                    ├── CD107a+
                    ├── IFNg+
                    ├── IL2+
                    ╰── TNFa+

Gather info for CD4+ cytokine gates

[32]:
cd4_child_gate_ids = workspace.get_child_gate_ids(sample_ids[0], 'CD4+')
[33]:
cd4_child_gate_ids
[33]:
[('CD107a+', ('root', 'Time', 'Singlets', 'aAmine-', 'CD3+', 'CD4+')),
 ('IFNg+', ('root', 'Time', 'Singlets', 'aAmine-', 'CD3+', 'CD4+')),
 ('IL2+', ('root', 'Time', 'Singlets', 'aAmine-', 'CD3+', 'CD4+')),
 ('TNFa+', ('root', 'Time', 'Singlets', 'aAmine-', 'CD3+', 'CD4+'))]
[34]:
cd4_gate_path = cd4_child_gate_ids[0][1]
[35]:
cd4_gate_path
[35]:
('root', 'Time', 'Singlets', 'aAmine-', 'CD3+', 'CD4+')
[36]:
cd4_child_gate_names = [i[0] for i in cd4_child_gate_ids]
[37]:
cd4_child_gate_names
[37]:
['CD107a+', 'IFNg+', 'IL2+', 'TNFa+']

Build Boolean combo data structures

[38]:
n_channels = len(cd4_child_gate_ids)
bool_combos = list(itertools.product([True, False], repeat=n_channels))
[39]:
bool_combos
[39]:
[(True, True, True, True),
 (True, True, True, False),
 (True, True, False, True),
 (True, True, False, False),
 (True, False, True, True),
 (True, False, True, False),
 (True, False, False, True),
 (True, False, False, False),
 (False, True, True, True),
 (False, True, True, False),
 (False, True, False, True),
 (False, True, False, False),
 (False, False, True, True),
 (False, False, True, False),
 (False, False, False, True),
 (False, False, False, False)]
[40]:
bool_gate_labels = []

for bc in bool_combos:
    # here we call the same function we created in the Session version
    tmp_label = generate_bool_gate_name(cd4_child_gate_names, bc, prefix='CD4')
    bool_gate_labels.append(tmp_label)
[41]:
bool_gate_labels
[41]:
['CD4:CD107a+_IFNg+_IL2+_TNFa+',
 'CD4:CD107a+_IFNg+_IL2+_TNFa-',
 'CD4:CD107a+_IFNg+_IL2-_TNFa+',
 'CD4:CD107a+_IFNg+_IL2-_TNFa-',
 'CD4:CD107a+_IFNg-_IL2+_TNFa+',
 'CD4:CD107a+_IFNg-_IL2+_TNFa-',
 'CD4:CD107a+_IFNg-_IL2-_TNFa+',
 'CD4:CD107a+_IFNg-_IL2-_TNFa-',
 'CD4:CD107a-_IFNg+_IL2+_TNFa+',
 'CD4:CD107a-_IFNg+_IL2+_TNFa-',
 'CD4:CD107a-_IFNg+_IL2-_TNFa+',
 'CD4:CD107a-_IFNg+_IL2-_TNFa-',
 'CD4:CD107a-_IFNg-_IL2+_TNFa+',
 'CD4:CD107a-_IFNg-_IL2+_TNFa-',
 'CD4:CD107a-_IFNg-_IL2-_TNFa+',
 'CD4:CD107a-_IFNg-_IL2-_TNFa-']

Analyze samples & extract cytokine gate membership arrays

Call analyze_samples() for the sample group and collectgate membership arrays for a Sample.

[42]:
workspace.analyze_samples('DEN')
[43]:
cd4_gate_memb = workspace.get_gate_membership(sample_ids[0], 'CD4+')
[44]:
cd4_event_count = cd4_gate_memb.sum()
print(cd4_event_count)
82484
[45]:
cd4_cytokine_gate_memb_lut = {}

for child_gate_name, child_gate_path in cd4_child_gate_ids:
    tmp_gate_memb = workspace.get_gate_membership(sample_ids[0], child_gate_name, child_gate_path)

    # filter for just the CD4+ events
    tmp_gate_memb = tmp_gate_memb[cd4_gate_memb]

    cd4_cytokine_gate_memb_lut[child_gate_name] = tmp_gate_memb
[46]:
cd4_cytokine_gate_memb_lut
[46]:
{'CD107a+': array([False, False, False, ..., False, False, False], shape=(82484,)),
 'IFNg+': array([False, False, False, ..., False, False, False], shape=(82484,)),
 'IL2+': array([False, False, False, ..., False, False, False], shape=(82484,)),
 'TNFa+': array([False, False, False, ..., False, False, False], shape=(82484,))}
[47]:
# Make into a DataFrame
df_cd4_cytokine_gate_memb = pd.DataFrame(cd4_cytokine_gate_memb_lut)
[48]:
df_cd4_cytokine_gate_memb.head()
[48]:
CD107a+ IFNg+ IL2+ TNFa+
0 False False False False
1 False False False False
2 False False False False
3 False False False False
4 False False False False

Create Boolean combos from the cytokine gate memberships

[49]:
# Compare underlying NumPy array to the list of Bool combos
cd4_cytokine_combo_result = np.all(df_cd4_cytokine_gate_memb.values[:, np.newaxis, :] == bool_combos, axis=2)
[50]:
# Create the new DataFrame
df_cd4_cytokine_bool_combos = pd.DataFrame(
    cd4_cytokine_combo_result,
    columns=bool_gate_labels
)
[51]:
df_cd4_cytokine_bool_combos
[51]:
CD4:CD107a+_IFNg+_IL2+_TNFa+ CD4:CD107a+_IFNg+_IL2+_TNFa- CD4:CD107a+_IFNg+_IL2-_TNFa+ CD4:CD107a+_IFNg+_IL2-_TNFa- CD4:CD107a+_IFNg-_IL2+_TNFa+ CD4:CD107a+_IFNg-_IL2+_TNFa- CD4:CD107a+_IFNg-_IL2-_TNFa+ CD4:CD107a+_IFNg-_IL2-_TNFa- CD4:CD107a-_IFNg+_IL2+_TNFa+ CD4:CD107a-_IFNg+_IL2+_TNFa- CD4:CD107a-_IFNg+_IL2-_TNFa+ CD4:CD107a-_IFNg+_IL2-_TNFa- CD4:CD107a-_IFNg-_IL2+_TNFa+ CD4:CD107a-_IFNg-_IL2+_TNFa- CD4:CD107a-_IFNg-_IL2-_TNFa+ CD4:CD107a-_IFNg-_IL2-_TNFa-
0 False False False False False False False False False False False False False False False True
1 False False False False False False False False False False False False False False False True
2 False False False False False False False False False False False False False False False True
3 False False False False False False False False False False False False False False False True
4 False False False False False False False False False False False False False False False True
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
82479 False False False False False False False False False False False False False False False True
82480 False False False False False False False False False False False False False False False True
82481 False False False False False False False False False False False False False False False True
82482 False False False False False False False False False False False False False False False True
82483 False False False False False False False False False False False False False False False True

82484 rows × 16 columns

[52]:
# Get the event counts using sum()
df_cd4_cytokine_bool_combos.sum()
[52]:
CD4:CD107a+_IFNg+_IL2+_TNFa+        0
CD4:CD107a+_IFNg+_IL2+_TNFa-        0
CD4:CD107a+_IFNg+_IL2-_TNFa+        0
CD4:CD107a+_IFNg+_IL2-_TNFa-        0
CD4:CD107a+_IFNg-_IL2+_TNFa+        0
CD4:CD107a+_IFNg-_IL2+_TNFa-        0
CD4:CD107a+_IFNg-_IL2-_TNFa+        0
CD4:CD107a+_IFNg-_IL2-_TNFa-       68
CD4:CD107a-_IFNg+_IL2+_TNFa+        0
CD4:CD107a-_IFNg+_IL2+_TNFa-        0
CD4:CD107a-_IFNg+_IL2-_TNFa+        4
CD4:CD107a-_IFNg+_IL2-_TNFa-        0
CD4:CD107a-_IFNg-_IL2+_TNFa+        1
CD4:CD107a-_IFNg-_IL2+_TNFa-        5
CD4:CD107a-_IFNg-_IL2-_TNFa+       16
CD4:CD107a-_IFNg-_IL2-_TNFa-    82390
dtype: int64
[53]:
# One final tip, use np.where to get the original indices if you need them.
# The original CD4+ gate membership array has length of the entire event array.
# The True values are what we filtered on, so those corresponde to the bool combo rows
np.where(cd4_gate_memb)
[53]:
(array([     6,      9,     10, ..., 290163, 290164, 290171],
       shape=(82484,)),)