iota
function (the analog in Python is called xrange
) and returns an array of 5x5x40. import std.range : iota; import std.experimental.ndslice; void main() { auto slice = sliced(iota(1000), 5, 5, 40); }
auto
keyword.sliced
function returns a multi-dimensional slice. At the input, it can take as simple arrays as well as ranges
. As a result, we have a 5x5x40 cube consisting of numbers from 0 to 999.front
, which returns the first element of the range, popFront
, which goes to the next element and empty
returns a boolean value indicating that the looped sequence is empty. Ranges
allow you to perform lazy brute-force access to data as they are needed. The concept of Ranges is covered in more detail here .iota
also allows you to generate lazy ranges, and sliced
in lazy mode also receives data from iota
and processes them as it is received.stdin
, filter only unique strings, sort them, and then return to stdout
. import std.stdio; import std.array; import std.algorithm; void main() { stdin // get stdin as a range .byLine(KeepTerminator.yes) .uniq // stdin is immutable, so we need a copy .map!(a => a.idup) .array .sort // stdout.lockingTextWriter() is an output range, meaning values can be // inserted into to it, which in this case will be sent to stdout .copy(stdout.lockingTextWriter()); }
slice
has three dimensions, this is a range that returns ranges of ranges (ranges of ranges). This is clearly seen in the following example: import std.range : iota; import std.stdio : writeln; import std.experimental.ndslice; void main() { auto slice = sliced(iota(1000), 5, 5, 40); foreach (item; slice) { writeln(item); } }
[[0, 1, ... 38, 39], [40, 41, ... 78, 79], [80, 81, ... 118, 119], [120, 121, ... 158, 159], [160, 161, ... 198, 199]] ... [[800, 801, ... 838, 839], [840, 841, ... 878, 879], [880, 881, ... 918, 919], [920, 921, ... 958, 959], [960, 961, ... 998, 999]]
foreach
works much like a for
in Python. However, in D you can use it both in C style and in Python cycle style, but without the hassle of enumerate
or xrange
. import std.range : iota; import std.experimental.ndslice; void main() { auto slice = 1000.iota.sliced(5, 5, 40); }
a.func(b)
func(a, b)
dub init
and in the file \source\app.d
we write: import std.experimental.ndslice; void main() { }
std.experimental.ndslice;
located in the std.experimental
section. This does not mean that it is raw. This means that he needs to insist. dub
a = numpy.arange(1000).reshape((5, 5, 40)) b = a[2:-1, 1, 10:20]
auto a = 1000.iota.sliced(5, 5, 40); auto b = a[2 .. $, 1, 10 .. 20];
import numpy data = numpy.arange(100000).reshape((100, 1000)) means = numpy.mean(data, axis=0)
import std.range; import std.algorithm.iteration; import std.experimental.ndslice; import std.array : array; void main() { auto means = 100_000.iota .sliced(100, 1000) .transposed .map!(r => sum(r) / r.length) .array; }
array
method. However, in a real application, the result will not be calculated until it is used in another part of the program.map!
function map!
has an exclamation mark at the end. This means that this is a template function. It allows at the compilation stage to generate code based on the expression specified in its body. Here's a good article on how the patterns themselves in D work .map!
the code will work with any input data that is a range. While the Python code will only work with special arrays from Numpy.iota
dynamically creates data that the sliced
function sliced
. And accordingly, we don’t touch the memory until its last relocation. Also D returns an array with a long
data type while Numpy is from double
. As a result, I rewrote the code and brought the value of the array to 1,000,000 instead of 10,000. Here’s what happened: import std.range : iota; import std.array : array; import std.algorithm; import std.datetime; import std.conv : to; import std.stdio; import std.experimental.ndslice; enum test_count = 10_000; double[] means; int[] data; void f0() { means = data .sliced(100, 10000) .transposed .map!(r => sum(r, 0L) / cast(double) r.length) .array; } void main() { data = 1_000_000.iota.array; auto r = benchmark!(f0)(test_count); auto f0Result = to!Duration(r[0] / test_count); f0Result.writeln; }
%timeit
function in D std.datetime.benchmark
to measure speed. Compiled everything using LDC v0.17 with the following keys: ldmd2 -release -inline -boundscheck=off -O
. Or if you are using a dub, then the equivalent of these keys will be the dub --build=release-nobounds --compiler=ldmd2
options dub --build=release-nobounds --compiler=ldmd2
. Python: 145 µs LDC: 5 µs D is 29x faster
Python: 1.43 msec LDC: 628 ÎĽs D is 2.27x faster
np.asarray
which will copy it into a new variable. A quick search on github showed that thousands of projects use this crutch. While data could simply be transferred from one function to another without these blank copies. import numpy as np a = [[0.2,0.5,0.3], [0.2,0.5,0.3]] p = np.asarray(a) y = np.asarray([0,1])
sum(a)
a.sum()
import std.range : iota; import std.algorithm.iteration : sum; import std.experimental.ndslice; void main() { auto slice = 1000.iota.sliced(5, 5, 40); auto result = slice // sum expects an input range of numerical values, so to get one // we call std.experimental.ndslice.byElement to get the unwound // range .byElement .sum; }
sum
function and it just takes and works, just like any other function from the base library. a = [0] * 1000
a = numpy.zeros((1000))
auto a = repeat(0, 1000).array;
auto a = repeat(0, 1000).array.sliced(5, 5, 40);
movingWindowByChannel
function can also be used in other filters where a sliding window is required. movingWindowByChannel
allows movingWindowByChannel
to move around the image using a sliding window. Each such window is passed to a filter that calculates pixel values ​​based on the selected zone. /** Params: filter = unary function. Dimension window 2D is the argument. image = image dimensions `(h, w, c)`, where is the number of channels in the image nr = number of rows in the window n = number of columns in the window Returns: image dimensions `(h - nr + 1, w - nc + 1, c)`, where is the number of channels in the image. Dense data layout is guaranteed. */ Slice!(3, C*) movingWindowByChannel(alias filter, C) (Slice!(3, C*) image, size_t nr, size_t nc) { // local imports in D work much like Python's local imports, // meaning if your code never runs this function, these will // never be imported because this function wasn't compiled import std.algorithm.iteration: map; import std.array: array; // 0. 3D // The last dimension represents the color channel. auto wnds = image // 1. 2D composed of 1D // Packs the last dimension. .pack!1 // 2. 2D composed of 2D composed of 1D // Splits image into overlapping windows. .windows(nr, nc) // 3. 5D // Unpacks the windows. .unpack // 4. 5D // Brings the color channel dimension to the third position. .transposed!(0, 1, 4) // 5. 3D Composed of 2D // Packs the last two dimensions. .pack!2; return wnds // 6. Range composed of 2D // Gathers all windows in the range. .byElement // 7. Range composed of pixels // 2D to pixel lazy conversion. .map!filter // 8. `C[]` // The only memory allocation in this function. .array // 9. 3D // Returns slice with corresponding shape. .sliced(wnds.shape); }
/** Params: r = input range buf = buffer with length no less than the number of elements in `r` Returns: median value over the range `r` */ T median(Range, T)(Range r, T[] buf) { import std.algorithm.sorting: sort; size_t n; foreach (e; r) { buf[n++] = e; } buf[0 .. n].sort(); immutable m = n >> 1; return n & 1 ? buf[m] : cast(T)((buf[m - 1] + buf[m]) / 2); }
void main(string[] args) { import std.conv: to; import std.getopt: getopt, defaultGetoptPrinter; import std.path: stripExtension; // In D, getopt is part of the standard library uint nr, nc, def = 3; auto helpInformation = args.getopt( "nr", "number of rows in window, default value is " ~ def.to!string, &nr, "nc", "number of columns in window, default value is equal to nr", &nc); if (helpInformation.helpWanted) { defaultGetoptPrinter( "Usage: median-filter [<options...>] [<file_names...>]\noptions:", helpInformation.options); return; } if (!nr) nr = def; if (!nc) nc = nr; auto buf = new ubyte[nr * nc]; foreach (name; args[1 .. $]) { import imageformats; // can be found at code.dlang.org IFImage image = read_image(name); auto ret = image.pixels .sliced(cast(size_t)image.h, cast(size_t)image.w, cast(size_t)image.c) .movingWindowByChannel !(window => median(window.byElement, buf)) (nr, nc); write_image( name.stripExtension ~ "_filtered.png", ret.length!1, ret.length!0, (&ret[0, 0, 0])[0 .. ret.elementsCount]); } }
Source: https://habr.com/ru/post/277715/
All Articles