Skip to content

Commit

Permalink
Merge branch 'release/v1.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
CiaranOMara committed Dec 9, 2019
2 parents f1ff3d1 + ad3a8a3 commit 242392d
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 158 deletions.
8 changes: 4 additions & 4 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
name = "Bedgraph"
uuid = "0bcc2ff6-69eb-520d-bede-0374fc5bd2fd"
authors = ["Ciarán O'Mara <[email protected]>"]
version = "1.1.1"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
version = "1.2.0"

[compat]
julia = "0.7, 1"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]
17 changes: 7 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,17 @@ records = read(file, Vector{Bedgraph.Record})
```julia
using Bedgraph

records = Vector{Record}()
open(file, "r") do io
records = Bedgraph.readRecords(io)
records = open(file, "r") do io
return read(io, Vector{Bedgraph.Record})
end
```

Alternatively you may want to read and process records individually.
```julia
open(file, "r") do io
while !eof(io)
record = readRecord(io)
if record != nothing
# Process record.
end
while !eof(seek(io, Bedgraph.Record))
record = read(io, Bedgraph.Record) #Note: no protection.
# Process record.
end
end
```
Expand All @@ -76,7 +73,7 @@ const firsts = [49302000, 49302300, 49302600, 49302900, 49303200, 49303500, 4930
const lasts = [49302300, 49302600, 49302900, 49303200, 49303500, 49303800, 49304100, 49304400, 49304700]
const values = [-1.0, -0.75, -0.50, -0.25, 0.0, 0.25, 0.50, 0.75, 1.00]

records = convert(Vector{Bedgraph.Record}, chroms, firsts, lasts, values)
records = Bedgraph.Record.(chroms, firsts, lasts, values)

sort!(records)

Expand All @@ -93,7 +90,7 @@ records = [Record("chr19", 49302000, 49302300, -1.0), Record("chr19", 49302300,
header = Bedgraph.generateBasicHeader("chr19", records[1].first, records[end].last, bump_forward=false)

open(output_file, "w") do io
write(io, header, records))
write(io, header, records)
end

```
Expand Down
4 changes: 4 additions & 0 deletions src/Bedgraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ include("header.jl")
include("reader.jl")
include("writer.jl")


import Base.convert
@deprecate convert(::Type{Vector{Record}}, chroms::AbstractVector{<:AbstractString}, firsts::AbstractVector{Int}, lasts::AbstractVector{Int}, values::AbstractVector{<:Real}) Record.(chroms, firsts, lasts, values)

end # module
32 changes: 22 additions & 10 deletions src/header.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
"A container for the bedGraph header."
mutable struct BedgraphHeader{T} #TODO: determine what and how this will be.
data::T
end
BedgraphHeader{T}() where T = BedgraphHeader{T}(T())
BedgraphHeader() = BedgraphHeader{Vector{String}}()

function Base.convert(::Type{String}, header::BedgraphHeader{Vector{String}}) :: String
"Push data into [`BedgraphHeader`](@ref) container."
function Base.push!(sink::BedgraphHeader, data)
push!(sink.data, data) #Note: converts data to sink.data's eltype.
end

"Convert [`BedgraphHeader`](@ref) to type `String`."
function Base.convert(::Type{String}, header::BedgraphHeader{<:AbstractVector{<:AbstractString}})

str = ""
for line in header.data
Expand All @@ -12,7 +21,8 @@ function Base.convert(::Type{String}, header::BedgraphHeader{Vector{String}}) ::
return str
end

function generateBasicHeader(records::Vector{Record}; bump_forward=true) :: BedgraphHeader{Vector{String}} #Note: we assume that records are sorted by chrom and left position.
"Generate a basic bedGraph header given a vector of [`Record`](@ref)s."
function generateBasicHeader(records::AbstractVector{Record}; bump_forward=true) #Note: we assume that records are sorted by chrom and left position.

chrom = records[1].chrom

Expand All @@ -27,26 +37,28 @@ function generateBasicHeader(records::Vector{Record}; bump_forward=true) :: Bedg
return BedgraphHeader(["browser position $chrom:$pos_start-$pos_end", "track type=bedGraph"])
end

generateBasicHeader(chrom::String, pos_start::Int, pos_end::Int; bump_forward=true) = generateBasicHeader([Record(chrom, pos_start, pos_end, 0)], bump_forward=bump_forward)
generateBasicHeader(chrom::AbstractString, pos_start::Int, pos_end::Int; bump_forward=true) = generateBasicHeader([Record(chrom, pos_start, pos_end, 0)], bump_forward=bump_forward)

function _readHeader(io) :: Vector{String}
"Seek and then read bedGraph header into sink."
function _readHeader(io::IO, sink)
position(io) == 0 || seekstart(io)

header = String[]
line = readline(io)

while !eof(io) && !isLikeRecord(line) # TODO: seek more rebust check.
push!(header, line)
push!(sink, line)
line = readline(io)
end

return header
return sink
end

function Base.read(io::IO, ::Type{BedgraphHeader{Vector{String}}}) :: BedgraphHeader{Vector{String} }
return BedgraphHeader(_readHeader(io))
"Read bedGraph header into sink."
function Base.read(io::IO, sink::Type{<:BedgraphHeader})
return _readHeader(io, sink())
end

function Base.write(io::IO, header::BedgraphHeader{Vector{String}})
"Write bedGraph header to `IO` as `String`."
function Base.write(io::IO, header::BedgraphHeader)
return Base.write(io, convert(String, header))
end
39 changes: 11 additions & 28 deletions src/helpers.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function _bump(records::Vector{Record}, b::Int) :: Vector{Record}
function _bump(records::AbstractVector{Record}, b::Int)

new_records = Vector{Record}(undef, length(records))

Expand All @@ -10,11 +10,11 @@ function _bump(records::Vector{Record}, b::Int) :: Vector{Record}
return new_records
end

_bumpForward(records::Vector{Record}) = _bump(records, 1)
_bumpBack(records::Vector{Record}) = _bump(records, -1)
_bumpForward(records::AbstractVector{Record}) = _bump(records, 1)
_bumpBack(records::AbstractVector{Record}) = _bump(records, -1)


function _range(record::Record; right_open=true) :: UnitRange{Int}
function _range(record::Record; right_open=true)

pos_start = right_open ? record.first : record.first + 1
pos_end = right_open ? record.last - 1 : record.last
Expand All @@ -23,7 +23,7 @@ function _range(record::Record; right_open=true) :: UnitRange{Int}
end


function _range(records::Vector{Record}; right_open=true) :: UnitRange{Int}
function _range(records::AbstractVector{Record}; right_open=true)

pos_start = _range(records[1], right_open=right_open)[1]
pos_end = _range(records[end], right_open=right_open)[end]
Expand All @@ -32,24 +32,7 @@ function _range(records::Vector{Record}; right_open=true) :: UnitRange{Int}
end


function Base.convert(::Type{Vector{Record}}, chroms::Vector{String}, firsts::Vector{Int}, lasts::Vector{Int}, values::Vector{T}) where {T<:Real}

len_chroms = length(chroms)

# Check that arrays are of equal length.
len_chroms == length(firsts) && length(lasts) == length(values) && len_chroms == length(values) || error("Vectors are of unequal lengths: chroms=$(length(chroms)), firsts=$(length(firsts)), lasts=$(length(lasts)), values=$(length(values))")

records = Vector{Record}(undef, len_chroms)

for (i, chrom, first, last, value) in zip(1:len_chroms, chroms, firsts, lasts, values)
records[i] = Record(chrom, first, last, value)
end

return records
end


function compress(chroms::Vector{String}, n::Vector{Int}, values::Vector{<:Real}; right_open = true, bump_back=true) :: Vector{Record}
function compress(chroms::AbstractVector{<:AbstractString}, n::AbstractVector{Int}, values::AbstractVector{<:Real}; right_open = true, bump_back=true)

ranges = Vector{UnitRange{Int}}()
compressed_values = Vector{Float64}()
Expand All @@ -58,7 +41,7 @@ function compress(chroms::Vector{String}, n::Vector{Int}, values::Vector{<:Real}
range_start = 1
push!(compressed_values, values[1])

for (index, value ) in enumerate(values)
for (index, value) in enumerate(values)
if value != compressed_values[end]
push!(ranges, n[range_start] : n[index - 1] )
push!(compressed_values, value)
Expand Down Expand Up @@ -95,10 +78,10 @@ function compress(chroms::Vector{String}, n::Vector{Int}, values::Vector{<:Real}

end

compress(chrom::String, n::Vector{Int}, values::Vector{T}; right_open = true, bump_back=true) where {T<:Real} = compress(fill(chrom, length(n)), n, values, right_open = right_open, bump_back = bump_back)
compress(chrom::AbstractString, n::AbstractVector{Int}, values::AbstractVector{<:Real}; right_open = true, bump_back=true) = compress(fill(chrom, length(n)), n, values, right_open = right_open, bump_back = bump_back)


function expand(records::Vector{Record}; right_open=true, bump_forward=true)
function expand(records::AbstractVector{Record}; right_open=true, bump_forward=true)

#TODO: ensure records are sorted with no overlap.

Expand All @@ -119,5 +102,5 @@ function expand(records::Vector{Record}; right_open=true, bump_forward=true)
return collect(total_range), values, chroms
end

expand(chrom::String, firsts::Vector{Int}, lasts::Vector{Int}, values::Vector{T}; right_open=true, bump_forward=true) where {T<:Real} = expand( fill(chrom, length(firsts)), firsts, lasts, values, right_open=right_open, bump_forward=bump_forward)
expand(chroms::Vector{String}, firsts::Vector{Int}, lasts::Vector{Int}, values::Vector{T}; right_open=true, bump_forward=true) where {T<:Real} = expand( convert(Vector{Record}, chroms, firsts, lasts, values), right_open=right_open, bump_forward=bump_forward)
expand(chrom::AbstractString, firsts::AbstractVector{Int}, lasts::AbstractVector{Int}, values::AbstractVector{<:Real}; right_open=true, bump_forward=true) = expand(fill(chrom, length(firsts)), firsts, lasts, values, right_open=right_open, bump_forward=bump_forward)
expand(chroms::AbstractVector{<:AbstractString}, firsts::AbstractVector{Int}, lasts::AbstractVector{Int}, values::Vector{<:Real}; right_open=true, bump_forward=true) = expand(Record.(chroms, firsts, lasts, values), right_open=right_open, bump_forward=bump_forward)
69 changes: 40 additions & 29 deletions src/reader.jl
Original file line number Diff line number Diff line change
@@ -1,36 +1,43 @@
# Check if the record data is in the four column BED format.
function isLikeRecord(line::String) :: Bool
return occursin(r"^\s*\S*(?=[A-Za-z0-9])\S*\s+(\d+)\s+(\d+)\s+(\S*\d)\s*$", line) # Note: is like a record.
"Check whether string is in the four column BED format."
function isLikeRecord(line::AbstractString)
return occursin(r"^\s*\S*(?=[A-Za-z0-9])\S*\s+(\d+)\s+(\d+)\s+(\S*\d)\s*$", line) # Note: is like a record.
end
isLikeRecord(io::IO) = isLikeRecord(String(take!(io)))

function isBrowser(line::String) :: Bool
return occursin(r"^browser", lowercase(line))
"Check whether string has broswer information."
function isBrowser(line::AbstractString)
return occursin(r"^browser", lowercase(line))
end

function isComment(line::String) :: Bool
"Check whether string is a comment."
function isComment(line::AbstractString)
return occursin(r"^\s*(?:#|$)", line)
end


function seekNextRecord(io::IO) :: Nothing
function seekNextRecord(io::IO)

pos = position(io)
initial = pos == 0 ? -1 : pos # Note: Allows for the fist line of headerless bedGraph file to be read.
line = ""

while !eof(io) && (!isLikeRecord(line) || pos == initial)
while !eof(io)
pos = position(io)
line = readline(io)
end

seek(io, pos)
if isLikeRecord(line)
break
end

end

return nothing
return seek(io, pos)
end

"Seek position of next [`Record`](@ref)."
function Base.seek(io::IO, ::Type{Record})
return seekNextRecord(io)
end

# Note: all options are placed in a single line separated by spaces.
function readParameters(io::IO) :: String
function readParameters(io::IO)
seekstart(io)

pos = position(io)
Expand All @@ -47,32 +54,36 @@ end

function readRecord(io::IO) :: Union{Nothing, Record}

line = readline(io)
line = IOBuffer(readline(io))

if isLikeRecord(line)
return Record(line)
return read(line, Record)
end

return nothing
end

function readRecords(io::IO) :: Vector{Record}
seekstart(io)
seekNextRecord(io)
"Read string into type's constructor."
function Base.read(io::IO, obj::Type{Record})
line = readline(io)
return obj(line)
end

records = Vector{Record}()
function readRecords(io::IO, sink)
seekstart(io)

while !eof(io)
record = readRecord(io)
if record != nothing
push!(records, record)
end
while !eof(seek(io, Record))
record = read(io, Record)
push!(sink, record) #Note: converts Record to sink's eltype.
end

return records
return sink

end

function Base.read(io::IO, ::Type{Vector{Record}}) :: Vector{Record}
return readRecords(io)
readRecords(io::IO, sink::Type) = readRecords(io::IO, sink())
readRecords(io::IO) = readRecords(io::IO, Vector{Record})

function Base.read(io::IO, ::Type{T}) where {T<:AbstractVector{Record}}
return readRecords(io, T)
end
Loading

2 comments on commit 242392d

@CiaranOMara
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register()

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/6441

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if Julia TagBot is installed, or can be done manually through the github interface, or via:

git tag -a v1.2.0 -m "<description of version>" 242392d109ce30c7cd59dd69321982d7fdcd52d7
git push origin v1.2.0

Please sign in to comment.