White cords seem to hold a special place in the pantheon of khipu cord colors. A disproportionate amount of cords are white, they frequently start a cord group, etc. A separate study of White cords is most definitely in order.
I think of White cords as having a special functional marker of some type. In this, their appears some level of agreement. As an example, Jon Clindaniel noted in his Harvard Ph.D. Thesis that white and brown cords might have arithmetic meaning as in White being the sum of brown plus another cord.
Let’s explore possible white cord function markers for a clue to their meaning.
2. Exploratory Data Analysis
2.1 Group Cord Color Sequences
Each cord group has a set of cords each with a color. Express this as a list of colors A, B, C etc. for each cord/color in the group. Let’s see what are the most common group cord sequences
Code
# Load Khipus(khipu_dict, all_khipus) = kamayuq.fetch_khipus()plotly.offline.init_notebook_mode(connected =False);# Ascher-Group Color Frequency returns a "markov" chain of colors for all group's sorted by frequency...from ascher_group_color_frequency import count_most_common_group_colors(color_rep_sorted_frequency, color_rep_sorted_markov) = count_most_common_group_colors()color_rep_frequency_width = [(color_string, count, len(color_string.split(" "))) for (color_string, count) in color_rep_sorted_frequency]df = pd.DataFrame(color_rep_frequency_width, columns = ['color_string', 'count', 'num_colors'])df.head(20)
color_string
count
num_colors
0
W
628
1
1
PK
368
1
2
AB
312
1
3
LK
291
1
4
W W W W W W
149
6
5
W W W W W W W
137
7
6
DB
125
1
7
W W W
106
3
8
MB
106
1
9
YB
97
1
10
W W
82
2
11
W B W
70
3
12
W:AB
58
1
13
AB AB AB
53
3
14
LB LB LB LB LB
52
5
15
W W W W
51
4
16
AB AB AB AB
44
4
17
W W W W W
41
5
18
W W W W W W W W
40
8
19
W AB
40
2
<Figure size 6000x3000 with 0 Axes>
You can see that White cord groups occur A LOT.
5 of the top 6 cord color sequences, are White Cords.
2.2 Initial Group Cord Color Sequences
When white cords are not in bands, they seem to occur at the beginning and end of groups (a title cord? an info cord?). Let’s explore their distribution to see if that statement holds true.
Let’s graph their frequency of occurence by position/cord group length.
The conclusion: From the above graphic, you can observe that White cords have a SMALL tendency to lie on the left or right of a cord group for small group lengths. There is a small amount of evidence that white cords are a group “title” or info block. While underwhelming, it is especially evident in those magic Inka ayllu numbers for groups of 6, 7 (1 white cord + 6 ayllu cords), 9 and 10 cords.
2.3 White Cord Values
Where do white cord’s mean cord values arise versus other cord color mean values?
Code
from statistics import meanall_pendant_cords = []for aKhipu in all_khipus: all_pendant_cords += aKhipu[:,:]zero_value_pendant_cords = [aCord for aCord in all_pendant_cords if aCord.knotted_value()==0 ]non_zero_pendant_cords = [aCord for aCord in all_pendant_cords if aCord.knotted_value() >0]def add_cord(dict, cord_color, cord_value):if cord_color indict.keys(): dict[cord_color].append(cord_value)else: dict[cord_color] = [cord_value]def num_color_occurrences(cord_dict): color_value = {}for cord_color, value_list in cord_dict.items(): color_value[cord_color] = math.log(len(value_list),10)return color_valuedef mean_color_value(cord_dict): color_value = []for cord_color, value_list in cord_dict.items(): color_value.append((cord_color, mean(value_list)))return color_valuedef cord_statistic(cord_dict): color_value = []for cord_color, value_list in cord_dict.items(): color_value.append((cord_color, [min(value_list), max(value_list), mean(value_list), math.stdev(value_list)]))return color_valuecord_sums = {}for aCord in non_zero_pendant_cords: add_cord(cord_sums, aCord.longest_ascher_color(), aCord.knotted_value())colors_by_value =sorted(mean_color_value(cord_sums), key=lambda x : x[1], reverse=True)colors_by_occurrence = num_color_occurrences(cord_sums)top_colors_by_value = colors_by_value[:75]top_color_occurrences = [colors_by_occurrence[aColor] for (aColor, count) in top_colors_by_value]
First let’s evaluate the cord value range/distribution for white cords.
Code
def cord_statistic(cord_dict): color_value = []for cord_color, value_list in cord_dict.items(): std_dev = statistics.stdev(value_list) iflen(value_list)>1else0 color_value.append((cord_color, [min(value_list), max(value_list), mean(value_list), std_dev]))return color_valuestats = cord_statistic(cord_sums)white_range = [(x,y) for (x,y) in stats if x=='W'][0]print(f"Range for white cords values [min, max, mean, std_dev] is {white_range}")color_sum_white_df = pd.DataFrame(cord_sums['W'],columns = ['cord_value'])fig = px.histogram(color_sum_white_df, x="cord_value", log_y=True, title="Histogram of White Cord Values - Count is Log_10", labels={"count":"Log_10 of Count"})fig.update_layout(width=944).show()
Range for white cords values [min, max, mean, std_dev] is ('W', [1, 320535, 439.6537875640493, 4535.173677379783])
White cords range from a low of 0/1 to 320,535, a mean of 440, and a very large standard deviation of 4,541.
Next, let’s examine white mean cord values compared to other colors.
Code
color_df_data =list(zip([aColor for (aColor, count) in top_colors_by_value], top_color_occurrences, [count for (aColor, count) in top_colors_by_value]))color_by_value_df = pd.DataFrame(color_df_data, columns = ['color', 'log_num_occurrences', 'mean_value']).sort_values(by=['log_num_occurrences'], ascending=False)color_by_value_df.head(50)
color
log_num_occurrences
mean_value
37
W
3.858597
439.653788
41
AB
3.617315
384.398503
49
B
3.107888
330.223869
54
W:MB
2.872156
317.491275
47
LB
2.665581
344.870410
32
NB
2.647383
468.324324
22
GG
2.439333
632.632727
42
YG
2.217484
379.072727
74
BG
2.195900
223.910828
21
W:GG
2.167317
689.523810
36
W-MB
2.086360
450.549180
44
W-AB
1.913814
358.548780
64
LG
1.875061
287.453333
43
W%MB
1.875061
377.506667
62
W-KB
1.857332
295.666667
19
W%AB
1.845098
813.685714
56
W-DB
1.770852
311.898305
71
W:NB
1.740363
235.381818
52
AB-MB
1.707570
320.862745
72
RM
1.707570
234.470588
50
GL
1.662758
329.782609
17
B:CB
1.477121
954.233333
66
AB-GG
1.447158
260.535714
28
W:BD
1.431364
525.111111
4
B:YG
1.431364
2980.148148
45
B:BB
1.397940
357.040000
5
BL:AB
1.380211
2419.083333
10
B:GG
1.361728
1598.000000
53
AB:GG:MB
1.255273
320.055556
70
EB
1.255273
242.777778
11
W-BS
1.204120
1421.375000
68
B:LB
1.204120
246.187500
18
BL
1.204120
833.875000
69
W-GG
1.146128
245.000000
23
B:GG:LB
1.113943
632.307692
60
W:GG:MB
1.113943
297.923077
57
B:G
1.079181
310.583333
34
RM-MB
1.041393
455.727273
67
RD
1.000000
259.000000
2
YG:BB
0.954243
4251.000000
15
AB:NB
0.903090
1203.500000
30
B-YG
0.903090
488.000000
25
W:AB:GG
0.903090
612.500000
33
YB:BL
0.778151
464.666667
13
0R
0.778151
1284.000000
20
AB%MB
0.698970
762.600000
6
B-GG
0.698970
2246.400000
65
B-BB
0.698970
287.200000
26
W%GG
0.602060
592.500000
51
GG-KB
0.477121
325.333333
Code
color_by_value_df = color_by_value_df.sort_values(by=['log_num_occurrences'], ascending=False)color_by_value_df.head(50)size_vec = [x for x inlist(color_by_value_df.log_num_occurrences.values)]fig = (px.scatter(color_by_value_df, x='color', y='mean_value', log_y=True, size=size_vec, color='log_num_occurrences', hover_name='color', hover_data=['mean_value', 'log_num_occurrences'], labels={"color": "Ascher Color", "mean_value": "Mean Cord Value", "log_num_occurrences":"Log_10 Occurrences"}, title="Top 75 Mean Cord Values by Color, Sorted by # Occurences", width=944, height=1944) .update_layout(showlegend=True, xaxis =dict(tickfont =dict(size=8))) .show())
color
log_num_occurrences
mean_value
37
W
3.858597
439.653788
41
AB
3.617315
384.398503
49
B
3.107888
330.223869
54
W:MB
2.872156
317.491275
47
LB
2.665581
344.870410
32
NB
2.647383
468.324324
22
GG
2.439333
632.632727
42
YG
2.217484
379.072727
74
BG
2.195900
223.910828
21
W:GG
2.167317
689.523810
36
W-MB
2.086360
450.549180
44
W-AB
1.913814
358.548780
64
LG
1.875061
287.453333
43
W%MB
1.875061
377.506667
62
W-KB
1.857332
295.666667
19
W%AB
1.845098
813.685714
56
W-DB
1.770852
311.898305
71
W:NB
1.740363
235.381818
72
RM
1.707570
234.470588
52
AB-MB
1.707570
320.862745
50
GL
1.662758
329.782609
17
B:CB
1.477121
954.233333
66
AB-GG
1.447158
260.535714
28
W:BD
1.431364
525.111111
4
B:YG
1.431364
2980.148148
45
B:BB
1.397940
357.040000
5
BL:AB
1.380211
2419.083333
10
B:GG
1.361728
1598.000000
70
EB
1.255273
242.777778
53
AB:GG:MB
1.255273
320.055556
11
W-BS
1.204120
1421.375000
68
B:LB
1.204120
246.187500
18
BL
1.204120
833.875000
69
W-GG
1.146128
245.000000
23
B:GG:LB
1.113943
632.307692
60
W:GG:MB
1.113943
297.923077
57
B:G
1.079181
310.583333
34
RM-MB
1.041393
455.727273
67
RD
1.000000
259.000000
2
YG:BB
0.954243
4251.000000
15
AB:NB
0.903090
1203.500000
30
B-YG
0.903090
488.000000
25
W:AB:GG
0.903090
612.500000
33
YB:BL
0.778151
464.666667
13
0R
0.778151
1284.000000
20
AB%MB
0.698970
762.600000
6
B-GG
0.698970
2246.400000
65
B-BB
0.698970
287.200000
26
W%GG
0.602060
592.500000
9
W:YB:BL
0.477121
1727.000000
Fascinating! Note how color-combinations take up the majority of the high values. Also note the position White plays - 37’th on the list.
Let’s look at the graph sorted by mean cord value:
Code
color_by_value_df = color_by_value_df.sort_values(by=['mean_value'], ascending=False)size_vec = [x for x inlist(color_by_value_df.log_num_occurrences.values)]fig = (px.scatter(color_by_value_df, x='color', y='mean_value', log_y=True, size=size_vec, color='log_num_occurrences', hover_name='color', hover_data=['mean_value', 'log_num_occurrences'], labels={"color": "Ascher Color", "mean_value": "Mean Cord Value (Log_10)", "log_num_occurrences":"Log_10 Occurrences"}, title="Top 75 Mean Cord Values by Color, Sorted by Mean Cord Value", width=944, height=1944) .update_layout(showlegend=True, xaxis =dict(tickfont =dict(size=8))) .show())
2.4 White Cord Values for Pendant Pendant Sums
We are finally in a position to evaluate Jon Clindaniel’s proposal that White cords are grammatical markers for sums. Let’s use the pendant-pendant-sums fieldmark (the most common sum relationship) as a proxy for this examination. First, let’s load the data and draw a similar graph to the one above - but this time we will evaluate how often the color occurs, rather than it’s mean value.
Code
from fieldmark_ascher_pendant_pendant_sum import Fieldmark_Pendant_Pendant_SumaFieldmark = Fieldmark_Pendant_Pendant_Sum()khipu_df = aFieldmark.dataframes[0].dataframesum_cord_df = aFieldmark.dataframes[1].dataframedef pendant_mean_by_color(aColor): cords_with_color_df = sum_cord_df[sum_cord_df.cord_color==aColor] pendant_color_values =list(cords_with_color_df.cord_value.values) the_mean_value = mean(pendant_color_values) iflen(pendant_color_values) >0else0return math.log(the_mean_value,10) if the_mean_value >0else0def pendant_color_occurrences(aColor): cords_with_color_df = sum_cord_df[sum_cord_df.cord_color==aColor] num_occurrences =len(cords_with_color_df)return num_occurrencesall_colors =set(sum_cord_df.cord_color.values)color_frequency = [(aColor, pendant_color_occurrences(aColor), pendant_mean_by_color(aColor)) for aColor in all_colors]color_frequency_df = pd.DataFrame(color_frequency, columns = ['color', 'num_occurrences', 'log_mean_value'])color_frequency_df = color_frequency_df.sort_values(by=['num_occurrences'], ascending=False);color_frequency_df = color_frequency_df[:75]legend_text ="<i style=\"font-size:10pt;\"> Size/Color = Mean(Cord Values)"+\" - Hover Over Circles to View Khipu/Cord Info</i>"size_vec = [math.log10(x) for x inlist(color_frequency_df.log_mean_value.values)]fig = px.scatter(color_frequency_df, x='color', y='num_occurrences', log_y=True, size=size_vec, color='num_occurrences', hover_name='color', hover_data=['log_mean_value', 'num_occurrences'], labels={"color": "Ascher Color",'num_occurrences':"Color Frequency", "log_mean_value": "Log_10 of Mean Cord Value", }, title=f"Pendant Sum Cords by Color Frequency (Top 75){legend_text}", width=944, height=944).update_layout(showlegend=True).show()
Here you can see that White predominates sum relationships. SUBSTANTIALLY. This lends credence to Dr. Clindaniel’s argument. But WAIT!…
Let’s do one more thing. Let’s compare this distribution to an overall distribution of pendant cords in general.
Code
from collections import OrderedDict, Counterdef build_pendant_color_frequencies(): ascher_colors = []for aKhipu in all_khipus: khipu_cords = aKhipu.pendant_cords()for cord_index, aCord inenumerate(khipu_cords): cord_colors = [aColor.full_color for aColor in aCord.ascher_cord_colors]iflen(cord_colors)>0: ascher_colors += cord_colorsreturn OrderedDict(Counter(ascher_colors).most_common()) pendant_color_frequency_dict = build_pendant_color_frequencies()pendant_sum_color_frequency_dict = OrderedDict(zip(color_frequency_df.color.values, color_frequency_df.num_occurrences.values))theta = ku.degrees_between_dicts(pendant_color_frequency_dict, pendant_sum_color_frequency_dict)print(f"Angle between all_colors vector and pendant pendant sum color vector is {theta:0.2f}° degrees")
Angle between all_colors vector and pendant pendant sum color vector is 4.35° degrees
That’s astounding. Pendant sum color frequency matches almost exactly (within 5° degrees) to overall pendant color frequency amongst all the khipus.
If we are to look at the predominance of white sums in the above scatterplot Top 75 Pendant Sum Cords by Color Frequency one can conclude that Dr. Clindaniel’s argument makes sense. However, the closeness of the vector angle disproves the case. If color was a grammatical marker, we would expect to see a more significant difference. Consequently, the apparent conclusion is that color does not play a grammatical role in summation patterns.
So far, two types of Ascher Sum Relationships have been politely ignored:
Colored Pendant Cord Sums (by Color) - Pendant cords that are Sums of a set of Contiguous Pendant cords, with the Same Color, ignoring parent cord group boundaries and
Indexed Pendant Sum Cords - Pendant cords that are Sums of Similarly Indexed cords in Contiguous Groups to the right or left of the cord.
In these type of cross-linked sums (as opposed to the contiguous sums of pendant-pendant sums), the grammatical marker is position, not color. As an example, look at the large cord value khipu UR1143 where the sums are indicated by position index and also by similar color.
Dr. Clindaniels argument would apply then, only to pendant pendant sums, and that conclusion now seems unlikely.
The reverse argument could also be made. The majority of pendants are sum cords. Is that true? Let’s look at just this fieldmark alone - pendant pendant sums, which has the most number of sum cords:
Code
num_khipu_pendants =sum([khipu_dict[khipu_name].num_pendant_cords() for khipu_name in sum_cord_df.name.values])num_sum_pendants =len(aFieldmark.dataframes[1].dataframe)print(f"num pendant sum_cords is {num_sum_pendants}, out of a set of {num_khipu_pendants} total sum khipus pendants")percentage = (100.0*num_sum_pendants/float(num_khipu_pendants))print(f"That's a ratio of {percentage:.2f}%")
num pendant sum_cords is 8097, out of a set of 1769959 total sum khipus pendants
That's a ratio of 0.46%
So 0.5% - less than 1/2 of 1% - of the cords in khipus having a pendant pendant sum cord relations ship have pendants that are sum cords. So that argument might have trouble bearing weight…
As a final confirmation - let’s look at the color distributions of the two sets of colors (pendant sum colors) vs (pendant colors). Click on each image to view it full-size.
Click on image to view larger
Click on image to view larger
3. Conclusions
While white cords comprise a third of all pendant cords, and of cord groups of white, we don’t yet know what their color significance is. One theory, that they are grammatical markers for addition, appears disproved. However, they do show interesting levels of positional significance, appearing at the end or beginning of cord groups that contain many sums (sum groups), and in certain spikes in larger groups (i.e. in odd positions more than even ones).