The index that replaces the text

The collections the saving was quoted for

A document array compressed to the entropy of its own symbols saves exactly what the collection's length distribution allows, and the measurements that showed it used four constructed collections. The estimate they ended on — a directory of source files is roughly log-normal, so its array falls about a bit short of the plain one, around 17% — can be checked on a real directory. A project's 222 source files fall short by 0.66 bits and save 10.5%, a fifth of which is only the rounding of ⌈log₂ d⌉. Across six real collections the skew of the lengths is worth between nothing and 0.76 bits, and the log-normal formula overstates it wherever short documents are common.

The array is the length distribution found that a document index’s document array, one entry per character naming the document it came from, holds exactly one symbol distribution: the collection’s document lengths, weighted by characters. A wavelet tree shaped by document frequency stores the array in about the zeroth-order entropy of that distribution, and nothing about the text inside the documents changes it. On thirty-two documents of equal length there is nothing to save. On thirty-two where one holds four fifths of the text, most of the array goes.

A saving quoted without its collection then caught a check reporting a saving of sixteen per cent on a collection that had none. The plain array spends ⌈log₂ d⌉ bits a symbol, and whenever the number of documents is not a power of two, any code that spends log₂ d or less passes a comparison against it on the rounding alone. It closed by admitting what its four collections were: constructions, chosen to isolate a mechanism, none of them real. It guessed at what a real one would give. A directory of source files is roughly log-normal in its sizes, which by the arithmetic of entropy puts its array about a bit below log₂ d, “so around 17% off the array”. This page checks the guess on real collections.

The collections, and why lengths are enough

Six real collections are measured, all frozen so the numbers do not drift. A project’s source files: every JavaScript file of one mid-sized project at one version, 222 of them, by size in bytes. The paragraphs of three fixed corpora kept for measurement — a corpus of essays, one of technical writing, and one of revisions of a single document — 522, 179 and 30 paragraphs. And the essays and technical corpora taken whole, 12 and 8 documents.

Nothing needs to be indexed to measure the array, because the array’s symbols are its lengths. The plain array costs ⌈log₂ d⌉ bits a symbol. The entropy of the character-weighted length distribution, H0, is what a perfect code would spend. A Huffman-shaped tree spends Σ L·|code| over the whole collection, at most a bit a symbol above H0. The earlier pages checked that identity on built indexes: the tree’s levels hold exactly the Huffman code’s bits. Here it is applied to lengths read off real collections.

The gap between the plain array and a frequency-shaped one has two parts that the earlier page separated and the estimate did not. The rounding, ⌈log₂ d⌉ − log₂ d, is what any code saves by not rounding the document number up to whole bits, and it needs no model of the collection at all. The skew, log₂ d − H0, is what the length distribution adds, and only a code that knows the lengths collects it.

About a tenth, not about a sixth

What a frequency-shaped document array saves on real collections: 10.5% on 222 source files of one project, where the earlier estimate for a directory of source files was about 17%, and between 2.1% and 16.5% across six collections — and on every one a share of the saving is only the rounding of ⌈log₂ d⌉, which 222 documents leave at 0.21 bits a symbol, 2.6% of the array, with no model at allThe saving of a Huffman-shaped document array over the plain one, which spends ⌈log₂ d⌉ bits a symbol, as a share of the plain array, split into the part that ⌈log₂ d⌉ − log₂ d accounts for and the part the length distribution's skew (log₂ d − H0) accounts for, less the Huffman code's redundancy over the entropy. The source files of one project: 222 documents, 10.5% saved — rounding 2.6%, skew 8.3%, redundancy −0.4%; Paragraphs of the essays corpus: 522 documents, 11.5% saved — rounding 9.7%, skew 2.1%, redundancy −0.3%; Paragraphs of the technical corpus: 179 documents, 11.9% saved — rounding 6.5%, skew 5.9%, redundancy −0.4%; Paragraphs of the revisions corpus: 30 documents, 16.5% saved — rounding 1.9%, skew 15.2%, redundancy −0.6%; The essays corpus, whole essays: 12 documents, 9.1% saved — rounding 10.4%, skew 0.1%, redundancy −1.4%; The technical corpus, whole documents: 8 documents, 2.1% saved — rounding 0.0%, skew 3.7%, redundancy −1.6%. The dashed line is the earlier estimate, 17%.rounding of ⌈log₂ d⌉skew of the lengthsthe estimate, 17%source files10.5% · d 222essay paragraphs11.5% · d 522technical paragraphs11.9% · d 179revision paragraphs16.5% · d 30whole essays9.1% · d 12whole technical documents2.1% · d 8share of the plain array's bitssource files at one version
Fig. 1 The saving of a frequency-shaped array over the plain one, as a share of the plain array, split into rounding and skew. Source files: 10.5% (rounding 2.6%, skew 8.3%). Essay paragraphs: 11.5% (9.7% and 2.1%). Technical paragraphs: 11.9% (6.5% and 5.9%). Revision paragraphs: 16.5% (1.9% and 15.2%). Whole essays: 9.1%, all rounding. Whole technical documents: 2.1%. Dashed: the estimate, 17%.

On the project’s 222 source files the frequency-shaped array is 10.5% smaller than the plain one. The skew of their sizes is worth 0.66 bits a symbol, not about one, and a fifth of the saving, 0.21 bits, is the rounding of eight bits down to log₂ 222. The estimate’s number, 17%, was a skew of about a bit against eight bits a symbol and nothing else. The real directory’s skew is two thirds of that, and it saves a tenth, not a sixth.

The other collections make the split plain. The paragraphs of the essays corpus save 11.5%, and almost all of it is rounding. 522 paragraphs sit just above 512, so the plain array spends ten bits where 9.03 would do. Their lengths are skewed by 0.21 bits, a fiftieth of the array. The whole essays, twelve of them, save 9.1%, and every bit of it is rounding, since twelve essays of nearly equal length have almost no skew at all. A check that compared the frequency-shaped array with the plain one on either collection would report a healthy saving and be reporting the document count.

Only the revisions corpus, cut into its thirty paragraphs, approaches the estimate: 16.5%, nearly all of it skew. It is also the smallest real collection, and its paragraphs include whole sections of a document revised many times.

What the lengths look like

Four real collections, each document by its size: the source files run from 377 bytes to 264,023, a factor of 700, and their middle half spans 2.36 times; the essay paragraphs, 14 to 984 characters with a middle half of 2.47 — a spread of that size is what decides the array's saving, and it is modest everywhereEach collection's document lengths, largest first, against the document's rank as a share of the collection, on a logarithmic scale. The source files of one project: 222 documents, largest 264,023, median 17,923, smallest 377. Paragraphs of the essays corpus: 522 documents, largest 984, median 274, smallest 14. Paragraphs of the technical corpus: 179 documents, largest 2,241, median 459, smallest 29. Paragraphs of the revisions corpus: 30 documents, largest 9,258, median 1,231, smallest 232.00.2000.4000.6000.8001document, by rank of size, as a share of the collectioncharacters or bytes (log scale)101001,00010,000100,0001,000,000source filesessay paragraphstechnical paragraphsrevision paragraphslargest firstlogarithmic scale
Fig. 2 Document lengths, largest first, on a logarithmic scale. Source files: 222 from 264,023 bytes to 377, median 17,923. Essay paragraphs: 522 from 984 characters to 14, median 274. Technical paragraphs: 179 from 2,241 to 29. Revision paragraphs: 30 from 9,258 to 232.

The source files run from 377 bytes to 264,023, a factor of 700, and the middle half of them spans a factor of only 2.4. That is the shape of a log-normal distribution with a modest spread: a few very large files, a few very small ones, and most within a factor of two or three of the median. The essay paragraphs look the same at a smaller scale, from 14 characters to 984, with a middle half spanning 2.5. The extremes are what a reader notices in a directory listing. The middle is what decides the entropy, and the middle is narrow.

A corpus that was not generated recorded this collection’s unease about measuring mechanisms on constructed data, and the settlement it reached: constructed objects isolate a mechanism, real ones say whether it matters. The four constructed collections were chosen to span the range the mechanism can take, from nothing to most of the array. Real collections do not span it. They sit in a narrow band near the bottom.

The log-normal arithmetic, checked

The log-normal arithmetic, checked: for lengths whose logarithms spread by σ, the document array's entropy falls short of log₂ d by about σ²/(2 ln 2) bits; the source files' σ of 0.90 predicts 0.58 and they fall short by 0.66; the paragraph collections fall short by less than predicted, because their logarithms lean left — many short paragraphs widen σ and hold few characters — and no collection here reaches the bit the estimate assumedFor each real collection, the deficit log₂ d − H0(D) against σ²/(2 ln 2), where σ is the standard deviation of the natural logarithms of the lengths; the line is equality, and the dashed line marks the deficit of one bit the earlier estimate assumed. The source files of one project: σ 0.900, predicted 0.584, measured 0.660; Paragraphs of the essays corpus: σ 0.672, predicted 0.326, measured 0.209; Paragraphs of the technical corpus: σ 0.986, predicted 0.701, measured 0.469; Paragraphs of the revisions corpus: σ 1.155, predicted 0.963, measured 0.758; The essays corpus, whole essays: σ 0.085, predicted 0.005, measured 0.005; The technical corpus, whole documents: σ 0.439, predicted 0.139, measured 0.111.00.2500.5000.750100.2500.5000.7501deficit predicted from the lengths' log spread, σ²/(2 ln 2)measured deficit, log₂ d − H0 (bits a symbol)source filesessay paragraphstechnical paragraphsrevision paragraphswhole essayswhole technical documentsline: equalitydashed: the one bit assumed
Fig. 3 Measured deficit, log₂ d − H0, against σ2/(2ln⁡2)\sigma^2/(2 \ln 2) from the spread of the log-lengths. Source files: σ 0.90, predicted 0.58, measured 0.66. Essay paragraphs: 0.67, 0.33, 0.21. Technical paragraphs: 0.99, 0.70, 0.47. Revision paragraphs: 1.16, 0.96, 0.76. Whole essays: 0.09, 0.005, 0.005. Dashed: the one bit the estimate assumed.

For log-normal lengths the arithmetic is exact in the limit: if the natural logarithms of the lengths spread with standard deviation σ, the character-weighted distribution falls short of log₂ d by σ2/(2ln⁡2)\sigma^2/(2 \ln 2) bits. The source files’ σ of 0.90 predicts 0.58 bits, and they fall short by 0.66. The estimate’s “about a bit” needs σ near 1.2, the spread of a directory whose middle half spans a factor of five. This one spans 2.4.

The formula comes from one observation about weighting. The document array counts each document once per character, so a document twice as long appears twice as often, and the distribution the array’s entropy is taken over is the length distribution tilted towards long documents. For log-normal lengths that tilt moves the mean of the logarithms up by σ2\sigma^2 and leaves their spread alone. The plain array is a code for the untilted, uniform choice of a document, and the gap between the two is the information the tilt adds: half of σ2\sigma^2 in natural units, σ2/(2ln⁡2)\sigma^2/(2 \ln 2) in bits. Nothing in it depends on d, which is why the formula is a limit. A small collection rarely contains the long documents that carry the tilt.

The paragraph collections fall short by less than the formula predicts, and the reason is the shape of their logarithms. The essays corpus’s paragraph lengths lean left: 41 of its 522 paragraphs are very short, headings and one-line transitions, and they widen σ while holding 1.5% of the characters. The deficit is set by the characters, so paragraphs that barely hold any hardly move it, while σ counts each paragraph once. On the technical paragraphs the lean is milder and so is the overstatement. A formula fitted to the spread of document counts is answering a different question from the one the array asks.

Fewer files, the same files

A smaller directory of the same files: drawn eight at a time the source files' deficit is 0.44 bits, and it climbs towards the log-normal figure as the directory grows, 0.66 at 128 files; the saving, which also carries ⌈log₂ d⌉'s rounding, moves the other way, from 13.0% at eight files to 9.0% at 128 — a quoted percentage depends on how many documents there happen to beRandom subsets of the project's source files, forty draws a size: the mean deficit log₂ d − H0, the mean σ²/(2 ln 2), and the mean saving of a Huffman-shaped array over ⌈log₂ d⌉ bits a symbol. 8 files: deficit 0.436, predicted 0.522, saved 13.0%; 16 files: deficit 0.526, predicted 0.530, saved 12.2%; 32 files: deficit 0.604, predicted 0.585, saved 11.4%; 64 files: deficit 0.626, predicted 0.565, saved 9.9%; 128 files: deficit 0.663, predicted 0.590, saved 9.0%; 222 files: deficit 0.660, predicted 0.584, saved 10.5%.00.2000.4000.6000.800source files in the directorybits a symbol816326412822213.0%12.2%11.4%9.9%9.0%10.5%deficit, measuredσ²/(2 ln 2)labels: the savingforty draws a size
Fig. 4 Random subsets of the source files, forty draws a size. Deficit: 0.44 bits at 8 files, 0.53 at 16, 0.60 at 32, 0.63 at 64, 0.66 at 128 and at all 222. σ2/(2ln⁡2)\sigma^2/(2 \ln 2): 0.52 to 0.59. Saving: 13.0%, 12.2%, 11.4%, 9.9%, 9.0%, 10.5%.

Drawn eight at a time, the source files fall 0.44 bits short of log₂ d. All of them fall 0.66 short, and the saving moves the other way, from 13.0% at eight files to 9.0% at 128. A small sample of a skewed distribution rarely includes its largest members, so its deficit is smaller, and the formula’s value is a limit approached as the directory grows. The saving is a share of ⌈log₂ d⌉ bits, and that denominator grows with d while the deficit levels off. So the same files, in a directory of a different size, would be quoted at a different percentage.

The revisions corpus’s thirty paragraphs show the effect from the other side. Its skew of 0.76 bits is the largest of any real collection here, and it comes from a collection small enough that one long section is a large share of the whole. Thirty paragraphs is where the constructed collections lived, at thirty-two documents, and it is where the most striking savings are found, because a small collection can be dominated by one member in a way a large one rarely is.

That is the earlier page’s warning in a new form. It found a check passing on the rounding whenever d was not a power of two. Here even the part of the saving that is real depends on d, falling as a share as a collection grows. A sixth of what, exactly found the same dependence in a reverse half’s saving, which was a different fraction at every sampling rate. A percentage quoted without its collection’s size, and without separating what the rounding gave, cannot be carried to another collection.

The rounding, taken without a model

The rounding is real bits, and a plain array can take it back without knowing anything about the lengths. It spends ⌈log₂ d⌉ bits on every entry because it stores each document number on its own. Store them in groups instead, k numbers as one integer below d^k, and each group needs ⌈k log₂ d⌉ bits. For the 522 essay paragraphs that is ten bits a symbol one at a time, 9.33 in groups of three and 9.13 in groups of eight, against log₂ 522 = 9.03. Grouping by eight takes back nine tenths of the rounding, about 9% of the array, with no model of the collection at all, at the price of decoding a group to read one entry.

For the 222 source files grouping does less. Log₂ 222 is 7.79, so groups of up to four still need eight bits a symbol, and groups of five reach 7.80. The rounding there was a fifth of the saving, and grouping by five takes almost all of it. For thirty paragraphs, log₂ 30 = 4.91, and no group of eight or fewer gets under five bits. Whether the rounding can be taken cheaply depends on how close d lies to a power of two, which is the earlier page’s point turned around: a power of two leaves nothing to round, and a number just above one leaves almost a whole bit.

A frequency-shaped tree takes the rounding and the skew together, and a code word is at least one bit found the price: a Huffman code cannot spend under one bit a symbol, which on these collections costs 0.03 to 0.06 bits above the entropy. Grouping the plain array costs no model and no redundancy, and gets only the rounding. The skew, the part that is about the collection, needs the shape.

How concentrated the characters are

How concentrated the characters are: the largest fifth of the project's source files hold 56.5% of its bytes, the largest fifth of the essay paragraphs 35.1% of their characters; in the constructed collections the earlier page measured, the largest fifth held 63.9% with lengths as one over rank and 84.2% with one document holding most — the real collections sit between the diagonal, where nothing is saved, and the constructed curve the estimate's arithmetic resembledThe share of all characters held by the largest documents, against the share of documents, largest first, for two real collections and two of the earlier page's constructed collections of 32 documents; the diagonal is equal lengths. Source files: largest tenth 41.7%, fifth 56.5%, half 80.6%; Essay paragraphs: largest tenth 19.5%, fifth 35.1%, half 71.7%; Lengths as one over rank: largest tenth 51.3%, fifth 63.9%, half 83.3%; One document holding most: largest tenth 82.3%, fifth 84.2%, half 89.9%.00.2500.5000.750100.2000.4000.6000.8001share of the documents, largest firstshare of the characters they holdsource filesessay paragraphslengths as one over rankone document holding mostdotted: equal lengthsdashed: constructed collections
Fig. 5 The share of the characters held by the largest documents. Source files: the largest tenth hold 41.7%, the largest fifth 56.5%. Essay paragraphs: 19.5% and 35.1%. The earlier page’s constructed collections: lengths as one over rank, 51.3% and 63.9%; one document holding most, 82.3% and 84.2%.

The largest fifth of the project’s source files hold 56.5% of its bytes. The largest fifth of the essay paragraphs hold 35.1% of their characters. Both sit between the diagonal of equal lengths and the constructed collection with lengths falling as one over rank, which held 63.9% in its largest fifth. Source files are more concentrated than prose paragraphs. A handful of large modules hold much of a project’s text, where no paragraph can run to many times the median without becoming a section. But neither comes near the constructed collection with one document holding most, whose largest fifth held 84.2%.

A curve like this is what a system would want to see before it relied on a document array’s saving, and it is cheap: one pass over the lengths. The entropy that decides the saving is the same pass, and the rounding needs only the count.

Real collections beside the constructed ones

Real collections against the constructed ones the saving was first measured on: the six real collections fall short of log₂ d by 0.00 to 0.76 bits, between equal lengths and lengths as one over rank, which falls short by 0.85; the constructed collection with one document holding most falls short by 3.31, a shape none of these real collections comes nearThe deficit log₂ d − H0(D), in bits a symbol, for the earlier page's four constructed collections of 32 documents and the six real collections here, in order. constructed: equal lengths: 0.000 (d 32); whole essays: 0.005 (d 12); whole technical documents: 0.111 (d 8); essay paragraphs: 0.209 (d 522); technical paragraphs: 0.469 (d 179); source files: 0.660 (d 222); constructed: a few long, many short: 0.690 (d 32); revision paragraphs: 0.758 (d 30); constructed: lengths as one over rank: 0.851 (d 32); constructed: one document holding most of the text: 3.312 (d 32).constructed, equal lengths0.000whole essays0.005whole technical documents0.111essay paragraphs0.209technical paragraphs0.469source files0.660constructed, few long, many short0.690revision paragraphs0.758constructed, one over rank0.851constructed, one holding most3.312bits a symbol short of log₂ dgrey: constructed
Fig. 6 Deficit in bits a symbol, in order. Constructed, equal lengths: 0. Whole essays: 0.005. Whole technical documents: 0.11. Essay paragraphs: 0.21. Technical paragraphs: 0.47. Source files: 0.66. Constructed, a few long and many short: 0.69. Revision paragraphs: 0.76. Constructed, lengths as one over rank: 0.85. Constructed, one document holding most: 3.31.

Every real collection falls between the constructed collection of equal lengths and the one with lengths as one over rank, 0.005 to 0.76 bits short of log₂ d against 0.85; the constructed collection with one document holding most falls short by 3.31, a shape nothing real here approaches. The four constructed collections were right to span the mechanism’s range. The measurement said what the range is. They were not a sample of what collections are like, and the one that produced the most dramatic plate is the one farthest from anything measured here.

The model is the compressor found five correct entropies for one stream, each a floor under a different model. The document array has one model, the length distribution, and one floor. What these collections show is where that floor sits in practice: a fraction of a bit below log₂ d, rarely more.

What the saving is a share of

The document array is, by the earlier pages’ accounting, the whole of the listing apparatus that is left. The apparatus three times smaller again took it from 839,000 bits to 120,000, and what remained was the one array that was ever information: the previous-occurrence chain went, the range minimum went, and the smaller tree hands it back unsorted measured what the frequency shape costs a listing query in sorting. So a tenth of the document array is a tenth of the apparatus that answers which documents hold a pattern. Beside the self-index that finds the occurrences, it is smaller again: on a collection where every character is indexed, the document array is one symbol per character of a few bits, where the self-index is several bits per character on its own.

That puts the real collections’ saving in proportion. On the project’s source files, coding the document array by its lengths would save about a tenth of a structure that is itself a fraction of the index. It is worth doing, since it costs one pass over the lengths and changes nothing about the answers. It is not the change that decides whether a document index is small, and an estimate of a sixth made it sound closer to that than it is.

The limits of the measurement

Six collections, one project’s sources. The source files are one project’s, written in one style by one set of habits. A directory of vendored libraries, generated code or data files would be more skewed, and a directory of small tests less. The measurement says what these collections give. It does not say what directories give in general.

Lengths, not indexes. The saving is computed from the lengths by the identity the earlier pages checked on built indexes. No document index over these collections was built, and the tree’s rank directory, which adds the same share to every shape, is not counted.

Bytes for characters. The source files are measured in bytes and the corpora in characters. A file with many characters outside the basic Latin range has more bytes than characters, and a document array over its characters would see it slightly smaller than its size here. In these files those characters are a few typographic marks in comments, and the difference to any file’s share is under a per cent.

Paragraphs are one way to cut. The paragraph collections split each corpus at blank lines. Cut at sections, or at sentences, the same text would be a different collection with a different distribution, and the saving belongs to the cut as much as to the text.

Still open: a collection that grows by appending

Every collection here is measured whole, and a real document collection changes. Documents are added, some large, most typical, and the array’s code has to follow its lengths or be rebuilt. The project’s source files grew from twenty to 222 over a few months, and their subsets show the deficit climbing as a directory grows.

The measurement that follows replays the same directory’s history, each file entering at the version where it first appeared and growing as it grew. It keeps a document array coded with the lengths as they stood at each rebuild, and charges each rebuild and each document added between rebuilds, which a code fitted earlier has to name with an escape. The prediction is that a code fitted once, when the directory held a quarter of its files, loses less than a tenth of a bit a symbol against one refitted at every version, because the log-normal shape of the sizes is set early and stays. It could fail if the large modules arrived late. Then a code fitted early would give them long codes, and the characters they hold would pay for it on every lookup.

Named alongside this one

Essays reaching for the same objects. Nobody chose these; they are what the concept index makes visible.

The objects this essay names

Each one links to every other essay that touches it.

CorpusDocument arrayDocument collectionEntropyIndex sizeMeasurement designRoundingWavelet tree