Percent of S and Z Knots


1. Percent of S and Z Knots

The percent of S knots vs Z knots in a khipu. Various theories about S and Z knots encoding moiety information or some other form of binary information exist in the literature. If you have not studied the distribution of recto and verso cord attachment, known to encode moiety information, please do so now.

Let’s explore a similar type of arrangement for S-knots vs Z-knots.

2. Summary Charts:

Summary Results:

Measure Result
Number of Khipus with S Knots 310 (48%)
Number of Khipus with Z Knots 387 (60%)
Five most Significant Khipu KH0081, KH0049, KH0569, KH0282, KH0271
Image Quilt Click here

Z Knot vs. S Knot Plots

Code
# Initialize plotly
import plotly
plotly.offline.init_notebook_mode(connected = False);

# Read in the Fieldmark and its associated dataframe and match dictionary
import qollqa_chuspa as qc
from fieldmark_khipu_summary import FieldmarkPercentSKnots, FieldmarkPercentZKnots
aFieldmark = FieldmarkPercentSKnots()
matching_khipus =   aFieldmark.matching_khipus() 

(khipu_dict, all_khipus) = qc.fetch_khipus()
khipu_summary_df = aFieldmark.get_khipu_summary_df()
khipu_summary_df['percent_s_knots'] = [round(100.0*x, 1) for x in list(khipu_summary_df.percent_s_knots.values)]
khipu_summary_df['percent_z_knots'] = [round(100.0*x, 1) for x in list(khipu_summary_df.percent_z_knots.values)]
khipu_summary_df.sort_values(by=['percent_s_knots'], inplace=True, ascending=False)

khipu_names = list(khipu_summary_df.kfg_name.values)
percent_s_knots = list(khipu_summary_df.percent_s_knots.values)
percent_z_knots = list(khipu_summary_df.percent_z_knots.values)
Code
from plotly import graph_objects as go
fig = (go.Figure()
         .add_trace(go.Bar(x=khipu_names,
                           y=percent_s_knots,
                           name='Percent S Knots',
                           marker_color='red'
                           ))
       .add_trace(go.Bar(x=khipu_names,
                         y=percent_z_knots,
                         name='Percent Z Knots',
                         marker_color='green'
                            ))
       .update_layout(barmode='group', width=1500, height=450, xaxis_tickangle=-90).show()
      )
Code
from plotly import express as px    

fig = px.scatter(khipu_summary_df, 
                 x='percent_z_knots', y='percent_s_knots', 
                 hover_name='kfg_name', hover_data=["percent_s_knots","percent_z_knots", "num_knots", "num_cords"],
                 size="num_cords", 
                 color='num_knots', 
                 labels={"percent_s_knots":"% S Knots", 'percent_z_knots':"% Z Knots"},
                 title=f"<b>% S-Knots/Z-Knots</b> - Size: # Cords, Color: Knot Count - Hover over circles for more information", 
                 width=944, height=944).update_layout(showlegend=True).show()

Clearly S and Z knots exhibit the same kind of marking/pairing that Verso and Recto attachments do!

3. Exploratory Data Analysis:

3.1 S-Knots on one side, Z-knots on the other…

In the history of decipherment, after numbers are decoded, the next semantic concept to be deciphered is usually dates.

Articles by Gary Urton, Alberto Saez-Rodríguez, and Juliana Martins have all proposed “calendar” khipus to describe an astronomical accounting of some “thing”. Saez-Rodriquez, for example, ties KH0118 to the Pleiades. Another khipu, KH0097 is also frequently mentioned in the literature.

These “astronomical” khipus use a spatial arrangement of S and Z knots in spatial quilt-like “blocks” (i.e. tokapu). Can we identify other khipus like this? Let’s use a simple hack, and look at the mean position of S knots and Z knots as a guide to searching out these kinds of khipus.

Code
# First cull database to khipus with at least a 70/30 or 30/70 mix of s and z knots
cutoff_threshold = 30.0
at_least_30_pct_s_df = khipu_summary_df[khipu_summary_df.percent_s_knots >= cutoff_threshold]
at_least_30_pct_s_and_z_knots_df = at_least_30_pct_s_df[at_least_30_pct_s_df.percent_z_knots >= cutoff_threshold]
fig = px.scatter(at_least_30_pct_s_and_z_knots_df, 
                 x='percent_z_knots', y='percent_s_knots', 
                 hover_name='kfg_name', hover_data=["percent_s_knots","percent_z_knots", "num_knots", "num_cords"],
                 size="num_cords", 
                 color='num_knots', 
                 labels={"percent_s_knots":"% S Knots", 'percent_z_knots':"% Z Knots"},
                 title=f"<b>Percent of Minimum of 30% S-Knots and Z-Knots</b> - Size is # Cords, Color is Knot Count", 
                 width=944, height=944).update_layout(showlegend=True).show()
len(at_least_30_pct_s_and_z_knots_df)
263
Code
from statistics import mean

search_khipus = list(at_least_30_pct_s_and_z_knots_df.kfg_name.values)
def knot_moment(knots):
    knot_cords = [aKnot.cord for aKnot in knots if not aKnot.cord.is_subsidiary_cord()]
    cord_indices = [aCord.pendant_index() for aCord in knot_cords]
    return round(mean(cord_indices),1) if len(cord_indices) > 0 else 0

calendar_candidates = []
for khipu_name in sorted(search_khipus):
    aKhipu = khipu_dict[khipu_name]
    s_moment = knot_moment(aKhipu.s_knots())
    z_moment = knot_moment(aKhipu.z_knots())
    mid_moment = aKhipu.num_pendant_cords()/2
    delta_s = abs(s_moment-mid_moment)
    delta_z = abs(z_moment-mid_moment)
    if  (delta_s > 2) and (delta_z > 2):
        if (s_moment < mid_moment) and (mid_moment < z_moment):
            calendar_candidates.append((khipu_name, s_moment, mid_moment, z_moment))
        if (z_moment < mid_moment) and (mid_moment < s_moment):
            calendar_candidates.append((khipu_name, s_moment, mid_moment, z_moment))

calendar_candidates_names = [x[0] for x in calendar_candidates]
calendar_candidates
[('AS074', 3.5, 46.5, 53.4),
 ('JC009', 42.5, 38.0, 35.7),
 ('JC010', 121, 80.0, 76.2),
 ('JC023', 21, 16.5, 12.7),
 ('KH0350', 19.5, 14.5, 12),
 ('QU011', 71.7, 59.0, 30.7),
 ('QU012', 76.9, 79.5, 83.0),
 ('UR017', 8.2, 48.5, 53.1),
 ('UR034', 50.5, 43.5, 40.9),
 ('UR050', 90.4, 82.0, 75.9),
 ('UR053C', 41.4, 30.5, 26.9),
 ('UR061', 18, 20.5, 23.3),
 ('UR063', 37.9, 55.5, 59.8),
 ('UR072', 46.5, 30.5, 27.2),
 ('UR087', 201, 153.0, 146.0),
 ('UR090', 26, 54.0, 57.0),
 ('UR1032', 344, 207.5, 175.3),
 ('UR1033F', 2, 10.5, 13.1),
 ('UR1114', 123.9, 169.5, 184.0),
 ('UR198', 143.9, 128.5, 120.1),
 ('UR269', 152.9, 144.5, 136.7),
 ('UR270', 108.4, 81.0, 77.0),
 ('UR274A', 53, 46.5, 43.2),
 ('UR274B', 53.5, 50.5, 46.7),
 ('UR283', 77.5, 91.5, 103.7),
 ('UR288', 9.5, 14.5, 18.8),
 ('UR292A', 36.2, 26.5, 24.4)]

Potential Calendar Khipus:

KH0599 KH0607 KH0300
KH0311 KH0033 KH0063
KH0097 KH0118 KH0349
KH0405 KH0433 KH0436
KH0437 KH0467 KH0521
KH0530
Code
candidate_mask = [name in calendar_candidates_names for name in list(khipu_summary_df.kfg_name.values)]
calendar_candidate_df = khipu_summary_df[candidate_mask]
fig = px.scatter(calendar_candidate_df, 
                 x='percent_z_knots', y='percent_s_knots', 
                 hover_name='kfg_name', hover_data=["percent_s_knots","percent_z_knots", "num_knots", "num_cords"],
                 size="num_cords", 
                 color='num_knots', 
                 labels={"percent_s_knots":"% S Knots", 'percent_z_knots':"% Z Knots"},
                 title=f"<b>Potential Calendar Khipu Candidates</b> - Size is # Cords, Color is Knot Count - Hover over circles for more information", 
                 width=944, height=944).update_layout(showlegend=True).show()

KH0063, KH0097, KH0118 and KH0349 all appear to be good candidates for a calendar khipu. Let’s take a quick look at them spatially:

Code
import math

def plot_s_z_knot_map(aKhipuName):
    def num_digits(aCluster):
        """ Returns 1 + the magnitude base 10 of knotted value """
        cluster_value = sum([aKnot.value for aKnot in aCluster.knots()])
        return 1 + int(math.floor(math.log10(cluster_value))) if cluster_value > 0 else 0

    def cord_state(col, row):
        theCord = thePendantCords[col]
        knots = theCord.all_knots(include_subsidiaries=False)
        the_state = 0
        if row < len(knots):
            row_knot = knots[row]
            if   row_knot.is_S_knot(): the_state =  1
            elif row_knot.is_Z_knot(): the_state = -1

        return (the_state)
    
    aKhipu = khipu_dict[aKhipuName]
    thePendantCords = aKhipu.pendant_cords()
    num_cols = len(thePendantCords)
    num_rows = max([aCord.num_knots() for aCord in thePendantCords])
    
    knot_states = [[cord_state(col_index, row_index)  for col_index in range(num_cols)] for row_index in range(num_rows,0,-1)]
    fig = go.Figure(data=go.Heatmap(z=knot_states, colorscale='Cividis_r', showlegend=False))
    fig.update_layout(showlegend=False, width=944, title=f"<b>{aKhipuName}</b> - Spatial Map of S vs Z Knots").show()
     
plot_s_z_knot_map('UR1104')
plot_s_z_knot_map('UR1084')
plot_s_z_knot_map('UR113')

3.2 S/Z Knots vs Banded/Seriated Khipus

Code
# Cull database to khipus with at least a 95/5 or 5/95 mix of s and z knots
cutoff_threshold = 5.0
at_least_5_pct_s_df = khipu_summary_df[khipu_summary_df.percent_s_knots >= cutoff_threshold]
at_least_5_pct_s_and_z_knots_df = at_least_5_pct_s_df[at_least_5_pct_s_df.percent_z_knots >= cutoff_threshold]

def banded_color(kfg_name): 
    aKhipu = khipu_dict[kfg_name]
    is_banded = aKhipu.num_banded_groups() > khipu_dict[kfg_name].num_seriated_groups()
    return 0.0 if is_banded else 1.0
def banded_ratio(kfg_name):
    aKhipu = khipu_dict[kfg_name]
    total_groups = aKhipu.num_cord_groups()
    return aKhipu.num_banded_groups()/total_groups if total_groups > 0 else 0

at_least_5_pct_s_and_z_knots_df['banded_color'] = [banded_color(x) for x in at_least_5_pct_s_and_z_knots_df.kfg_name.values]
at_least_5_pct_s_and_z_knots_df['banded_ratio'] = [banded_ratio(x) for x in at_least_5_pct_s_and_z_knots_df.kfg_name.values]

fig = px.scatter(at_least_5_pct_s_and_z_knots_df, 
                 x='num_seriated_groups', y='num_banded_groups', 
                 hover_name='kfg_name', hover_data=["num_banded_groups", "num_seriated_groups", "percent_s_knots","percent_z_knots", "num_knots", "num_cords"],
                 size="percent_s_knots", 
                 color='banded_color', color_continuous_scale=['#3c3fff', '#ff3030',],
                 labels={"percent_s_knots":"% S Knots", 'percent_z_knots':"% Z Knots"},
                 title=f"<b>S-Knots and Z-Knots by Banded/Seriated</b> - Blue=Banded, Red=Seriated, Size=%S Knots", 
                 width=944, height=944).update_coloraxes(showscale=False).show()

This chart shows the use of mixed S-Z knots is a pattern that occurs in both banded and seriated khipus.

4. Conclusions:

Clearly S and Z knots have a relationship to each other as a form of marking equivalent to Verso and Recto cords. However, studies by other khipu scholars indicate that S and Z knots appear to have a spatial significance in terms of their position on the khipu.

A simple test to find khipus that maybe astronomical in nature exists - use the spatial separation of S and Z knots as an indicator. Doing so, indicates that, at a minimum, KH0063, KH0097, KH0118 and KH0349 appear to be good candidates for a calendar khipu.