The centroid of a list of points - list

New to Haskell
The problem is
-- The centroid of a list of points is a point whose x (and y) coordinates are
-- the means of the x (and y) coordinates of the points in the list.
--
-- You may assume the list contains at least one point.
--
-- > centroid [Pt 1 1, Pt 2 2]
-- Pt 1.5 1.5
-- > centroid [Pt (-1.5) 0, Pt 3 2, Pt 0 1]
-- Pt 0.5 1.0
Try to code like this
data Point = Pt Double Double deriving (Show, Eq)
centroid :: [Point] -> Point
pointX :: Point -> Double
pointX (Pt x y) = x
pointY :: Point -> Double
pointY (Pt x y) = y
pointsX :: [Point] -> [Double]
pointsX xs = map pointX xs
pointsY :: [Point] -> [Double]
pointsY xs = map pointY xs
average :: [Double] -> Double
average xs = (sum xs) `div` (genericLength xs)
centroid cenpoint = (Pt average(pointsX cenpoint) average(pointsY cenpoint))
And I got
Project1.hs:35:22: error:
• Couldn't match expected type ‘([Double] -> Double)
-> [Double] -> Point’
with actual type ‘Point’
• The function ‘Pt’ is applied to four arguments,
but its type ‘Double -> Double -> Point’ has only two
In the expression:
(Pt average (pointsX cenpoint) average (pointsY cenpoint))
In an equation for ‘centroid’:
centroid cenpoint
= (Pt average (pointsX cenpoint) average (pointsY cenpoint))
|
35 | centroid cenpoint = (Pt average(pointsX cenpoint) average(pointsY cenpoint))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Project1.hs:35:25: error:
• Couldn't match expected type ‘Double’
with actual type ‘[Double] -> Double’
• Probable cause: ‘average’ is applied to too few arguments
In the first argument of ‘Pt’, namely ‘average’
In the expression:
(Pt average (pointsX cenpoint) average (pointsY cenpoint))
In an equation for ‘centroid’:
centroid cenpoint
= (Pt average (pointsX cenpoint) average (pointsY cenpoint))
|
35 | centroid cenpoint = (Pt average(pointsX cenpoint) average(pointsY cenpoint))
| ^^^^^^^
Project1.hs:35:33: error:
• Couldn't match expected type ‘Double’ with actual type ‘[Double]’
• In the second argument of ‘Pt’, namely ‘(pointsX cenpoint)’
In the expression:
(Pt average (pointsX cenpoint) average (pointsY cenpoint))
In an equation for ‘centroid’:
centroid cenpoint
= (Pt average (pointsX cenpoint) average (pointsY cenpoint))
|
35 | centroid cenpoint = (Pt average(pointsX cenpoint) average(pointsY cenpoint))
|

You were doing pretty well - note how the compilation errors all point to your final line (the definition of centroid). And they're all due to incorrect bracketing. This is what it should be instead:
centroid cenpoint = Pt (average (pointsX cenpoint)) (average (pointsY cenpoint))
That is, the x co-ordinate of the resulting Pt value is the average of the x co-ordinates of the corresponding points (pointsX cenpoint is the list of x-coordinates, so average (pointsX cenpoint) is their average), and similarly for the y co-ordinate.
Comparing to your incorrect version (which I have tidied up a little without changing how it is interpreted by the compiler):
Pt average (pointsX cenpoint) average (pointsY cenpoint)
This means you apply the Pt function to 4 arguments: average, pointsX cenpoint, average again, and pointsY cenpoint. This simply doesn't work because the Pt function only takes 2 arguments.

The main problem here is that you call the functions, like you would do in a language like Java, C++, or Python.
You need to wrap average into parenthesis, like:
centroid cenpoint = Pt (average (pointsX cenpoint)) (average (pointsY cenpoint))
As #dfeuer says, the outer parenthesis are not necessary here.
Furthermore you probably want to use (/) here over div, since you are working with Doubles, whereas div works on Integral types:
average :: [Double] -> Double
average xs = sum xs / genericLength xs
but like #leftroundabout says, we can better use length, and then use fromIntegral:
average :: [Double] -> Double
average xs = sum xs / fromIntegral (length xs)

Not really an answer to the question, but – this is the preferred way of doing it:
{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
import Data.VectorSpace
import GHC.Generics
data Point = Pt Double Double
deriving (Eq, Show, Generic, AdditiveGroup, VectorSpace)
average :: (VectorSpace v, Fractional (Scalar v)) => [v] -> v
average ps = sumV ps ^/ fromIntegral (length xs)
Then you can directly do
> average [Pt (-1.5) 0, Pt 3 2, Pt 0 1]
Pt 0.5 1.0
I.e. centroid ≡ average.

Related

How can you create a list comprising of calculations on each item of another list in Haskell?

I'm trying to make a function working out (and then outputting as a String) the difference between two elements in a list - 1st and 2nd, then 2nd and 3rd, and so on - I think I'm giving it a good go, but I currently keep running into error whack-a-mole, I've put the current error below, but first, obligatory code dump:
type Name = String
type Coordinates = (Int, Int)
type Pop = Int
type TotalPop = [Pop]
type City = (Name, (Coordinates, TotalPop))
testData :: [City]
testData = [("New York City", ((1,1), [5, 4, 3, 2])),
("Washingotn DC", ((3,3), [3, 2, 1, 1])),
("Los Angeles", ((2,2), [7, 7, 7, 5]))]
getPopGrowth :: [City] -> Name -> String
getPopGrowth cs name = concat
[getPercentages z ++ "\n" | (x,z) <- maybeToList (lookup name cs)] where
getPercentages z = unwords (map show z1) ++ "% " where
z1 = percentageIncrease z
percentageIncrease :: [Int] -> [Float]
percentageIncrease (x:xs)
| length (x:xs) > 2 = percentageIncrease (tail xs)
| otherwise = (a / b - 1) * 100.0 where
a = fromIntegral x :: Float
b = fromIntegral (head xs) :: Float
And the error I'm getting at the moment is:
error:
• Couldn't match expected type ‘[Float]’ with actual type ‘Float’
• In the expression: (a / b - 1) * 100.0
In an equation for ‘percentageIncrease’:
percentageIncrease (x : xs)
| length (x : xs) > 2 = percentageIncrease (tail xs)
| otherwise = (a / b - 1) * 100.0
where
a = fromIntegral x :: Float
b = fromIntegral (head xs) :: Float
|
92 | | otherwise = (a / b - 1) * 100.0 where
| ^^^^^^^^^^^^^^^^^^^
I would like to emphasise, I understand the error, but I do not know how to resolve it in such a way that I get the desired outcome of the function.
Just for some clarity around what I'm trying to do.
Input: getPopGrowth testData "New York City"
should Output: 25% 33.333% 50%
So far, you only calculate the percentage when the list has exactly two elements left. Less elements are not covered, and for longer lists, in all the steps before, the elements get dropped without further action. In the last step, however, you return a single Float instead of a list.
The following example creates an increase percentage in every step, concatenating it with the list resulting from applying the function to the tail of the list. Also, the base cases are all covered:
percentageIncrease :: [Int] -> [Float]
percentageIncrease [] = []
percentageIncrease (x:[]) = []
percentageIncrease (x:y:xs) = ((a / b - 1) * 100.0) : percentageIncrease (y:xs) where
a = fromIntegral x :: Float
b = fromIntegral y :: Float
Console output:
*Main> getPopGrowth testData "New York City"
"25.0 33.333336 50.0% \n"

Average of list of doubles

I am having some issues writing a Haskell function. I am trying to get the average of a list of triples of doubles. when I divide by just the ' length xs ' , I get an error that the length is an Int, so I tried using fromIntegeral and its not giving me an error but not giving the right answer. For example if I test,
averages [(2.0,1.0,3.0)] it returns [6.0]
averages :: [(Double, Double, Double)] -> [Double]
averages xs = [ (x+y+z) / fromIntegral (length xs) | (x,y,z) <- xs ]
You are missunderstanding your own type function, the avarage is always over 3, because you have a list of triplets (or tuples of three?):
averages :: [(Double, Double, Double)] -> [Double]
averages xs = [ (x+y+z) / 3 | (x,y,z) <- xs ]
$> averages [(8,6,7), (4,4,10), (1,1,1)]
=> [7.0,6.0,1.0]
Note that length xs will evaluate to 1 when you evaluate averages [(2.0,1.0,3.0)]. The list contains a single element: a tuple. Since you are averaging the elements of a triplet, you can just divide by 3.
Others have explained why you're getting the wrong answers, but I would like to urge you to break up your problem into two pieces.
averageTriple :: (Double, Double, Double) -> Double
averageTriple (x,y,z) = ...
averageTriples :: [(Double, Double, Double)] -> [Double]
averageTriples ts = ... -- use averageTriple here

Adding a list of points together

How do I add all of the x coordinates together in a list of points type Pt?
Example:
[Pt 1 2, Pt 3 4] => 1 + 3 => 4
Here is the data-type and function declaration that I am using:
data Point = Pt Double Double
x :: [Point] -> Point
This is what I have tried.
x :: [Point] -> Point
x [Pt x y] = Pt(x+x)
Probably the easiest way to do this, is by first making a function that can extract the x-coordinate of a point. For example with xPt:
xPt :: Point -> Double
xPt (Pt x _) = x
then we can make use of sum :: (Foldable f, Num a) => f a -> a and map :: (a -> b) -> [a] -> [b] to map a list of points to a list of x-coordinates:
xSum :: [Point] -> Double
xSum = sum . map xPt
You can use a list comprehension for this:
xSum :: [Point] -> Double
xSum pts = sum [x | Pt x _ <- pts]

Maximal positive submatrices using haskell

I have following problem:
You are given matrix m*n and you have to find maximal positive ( all elements of submatrix should be > 0) submatrices from (1,1) to (x,y).
What do I mean by maximal is, when you have following matrix:
[[1,2,3,4],[5,6,7,8],[9,10,-11,12],[13,14,15,16]]
then maximal positive submatrices are:
[[[1,2,3,4],[5,6,7,8]],[[1,2],[5,6],[9,10],[13,14]]]
i.e. first two rows is one solution and first two columns is second solution.
Another example: matrix is
[[1,2,3,-4],[5,6,7,8],[-9,10,-11,12],[13,14,15,16]]
and solution is:
[[[1,2,3],[5,6,7]]]
This is my Haskell program which solves it:
import Data.List hiding (insert)
import qualified Data.Set as Set
unique :: Ord a => [a] -> [a]
unique = Set.toList . Set.fromList
subList::[[Int]] ->[[[Int]]]
subList matrix = filter (allPositiveMatrix) $ [ (submatrix matrix 1 1 x y) | x<-[1..width(matrix)], y<-[1..height(matrix)]]
maxWidthMat::[[[Int]]] -> Int
maxWidthMat subList =length ((foldl (\largestPreviousX nextMatrix -> if (length (nextMatrix!!0)) >(length (largestPreviousX !!0)) then nextMatrix else largestPreviousX ) [[]] subList)!!0)
maxWidthSubmatrices:: [[[Int]]] -> Int ->[[[Int]]]
maxWidthSubmatrices subList maxWidth = filter (\x -> (length $x!!0)==maxWidth) subList
height matrix = length matrix
width matrix = length (matrix!!0)
maximalPositiveSubmatrices matrix = maxWidthSubmatrices (subList matrix) (maxWidthMat (filter (\x -> (length $x!!0)==( maxWidthMat $ subList matrix )) (subList matrix)))
allPositiveList list = foldl (\x y -> if (y>0)&&(x==True) then True else False) True list
allPositiveMatrix:: [[Int]] -> Bool
allPositiveMatrix matrix = foldl (\ x y -> if (allPositiveList y)&&(x==True) then True else False ) True matrix
submatrix matrix x1 y1 x2 y2 = slice ( map (\x -> slice x x1 x2) matrix) y1 y2
slice list x y = drop (x-1) (take y list)
maximalWidthSubmatrix mm = maximum $ maximalPositiveSubmatrices mm
maximalHeigthSubmatrix mm = transpose $ maximum $ maximalPositiveSubmatrices $ transpose mm
-- solution
solution matrix =unique $ [maximalWidthSubmatrix matrix]++[maximalHeigthSubmatrix matrix]
As you can see it's extremely lengthy and ugly.
It problably isn't fastest too.
Could you show me more elegant, faster and shorter solution ( possibly with explantions) ?
Proposed algorithm
I think that in order to solve the problem, we first better perform a dimension reduction:
reduce_dim :: (Num a,Ord a) => [[a]] -> [Int]
reduce_dim = map (length . takeWhile (>0)) -- O(m*n)
Here for every row, we calculate the number of items - starting from the left - that are positive. So for the given matrix:
1 2 3 4 | 4
5 6 7 8 | 4
9 10 -11 12 | 2
13 14 15 16 | 4
The second row thus maps to 2, since the third element is -11.
Or for your other matrix:
1 2 3 -4 | 3
5 6 7 8 | 4
-9 10 -11 12 | 0
13 14 15 16 | 4
Since the first row has a -4 at column 4, and the third one at column 1.
Now we can obtain a scanl1 min over these rows:
Prelude> scanl1 min [4,4,2,4] -- O(m)
[4,4,2,2]
Prelude> scanl1 min [3,4,0,4] -- O(m)
[3,3,0,0]
Now each time the number decreases (and at the end), we know we have found a maximal submatrix at the row above. Since that means we now work with a row from where on, the number of columns is less. Once we reach zero, we know that further evaluation has no sense, since we are working with a matrix with 0 columns.
So based on that list, we can simply generate a list of tuples of the sizes of the maximal submatrices:
max_sub_dim :: [Int] -> [(Int,Int)]
max_sub_dim = msd 1 -- O(m)
where msd r [] = []
msd r (0:_) = []
msd r [c] = [(r,c)]
msd r (c1:cs#(c2:_)) | c2 < c1 = (r,c1) : msd (r+1) cs
| otherwise = msd (r+1) cs
So for your two matrices, we obtain:
*Main> max_sub_dim $ scanl1 min $ reduce_dim [[1,2,3,4],[5,6,7,8],[9,10,-11,12],[13,14,15,16]]
[(2,4),(4,2)]
*Main> max_sub_dim $ scanl1 min $ reduce_dim [[1,2,3,-4],[5,6,7,8],[-9,10,-11,12],[13,14,15,16]]
[(2,3)]
Now we only need to obtain these submatrices themselves. We can do this by using take and a map over take:
construct_sub :: [[a]] -> [(Int,Int)] -> [[[a]]]
construct_sub mat = map (\(r,c) -> take r (map (take c) mat)) -- O(m^2*n)
And now we only need to link it all together in a solve:
-- complete program
reduce_dim :: (Num a,Ord a) => [[a]] -> [Int]
reduce_dim = map (length . takeWhile (>0))
max_sub_dim :: [Int] -> [(Int,Int)]
max_sub_dim = msd 1
where msd r [] = []
msd r (0:_) = []
msd r [c] = [(r,c)]
msd r (c1:cs#(c2:_)) | c2 < c1 = (r,c1) : msd (r+1) cs
| otherwise = msd (r+1) cs
construct_sub :: [[a]] -> [(Int,Int)] -> [[[a]]]
construct_sub mat = map (\(r,c) -> take r (map (take c) mat))
solve :: (Num a,Ord a) => [[a]] -> [[[a]]]
solve mat = construct_sub mat $ max_sub_dim $ scanl1 min $ reduce_dim mat
Which then generates:
*Main> solve [[1,2,3,4],[5,6,7,8],[9,10,-11,12],[13,14,15,16]]
[[[1,2,3,4],[5,6,7,8]],[[1,2],[5,6],[9,10],[13,14]]]
*Main> solve [[1,2,3,-4],[5,6,7,8],[-9,10,-11,12],[13,14,15,16]]
[[[1,2,3],[5,6,7]]]
Time complexity
The algorithm runs in O(m×n) with m the number of rows and n the number of columns, to construct the dimensions of the matrices. For every defined function, I wrote the time complexity in comment.
It will take O(m2×n) to construct all submatrices. So the algorithm runs in O(m2×n).
We can transpose the approach and run on columns instead of rows. So in case we are working with matrices where the number of rows differs greatly from the number of columns, we can first calculate the minimum, optionally transpose, and thus make m the smallest of the two.
Point of potential optimization
we can make the algorithm faster by constructing submatrices while constructing max_sub_dim saving some work.

Convert float to float option

How can I convert float to float option. Here is the function:
let variance (list_ : float list) : float option =
if List.length list_ < 2 then None
else begin
let average tmp_list = List.fold_left(+.) 0. tmp_list /. float(List.length tmp_list) in
let square value = value *. value in
let rec sum tmp_list =
match tmp_list with
| [] -> 0.0
| head::tail -> square (head -. average tmp_list) +. sum tail
in Some (sum list_ /. float(List.length list_))
end
I need sum list_ /. float(List.length list_) to return float option instead of float and I don't know how to do it. I thought if I return float in a float option function it would automatically return float option but I get the following error:
Error: This expression has type float but an expression was expected
of type float option
I have tried wrapping it around Some and failed. Can't seem to find anything on the internet.
EDIT: Added the full code
Your code runs, it just gives the wrong answer. That's because you sum function is incorrect : you should always use the average of the whole list, whereas you use the average from the current element onwards.
let variance l =
let sum l = List.fold_left (+.) 0. l in
let average l =
match List.length l with
0 -> None
| n -> Some (sum l /. float_of_int n) in
match average l with
None -> None
| Some m -> average (List.map (fun x -> (x -. m)**2.) l)